ruvector/examples/pwa-loader/sw.js
Claude 509987ad1c
feat: QR encoder, PWA loader, no_std fixes (swarm WIP)
QR encoder (feature-gated behind `qr`):
- Pure-Rust QR code encoder with GF(2^8) Reed-Solomon
- SVG and ASCII renderers
- Version 1-5 support, byte mode, EC level M
- Example: qr_seed_encode

PWA loader:
- Browser-based RVF seed decoder (HTML/JS/CSS)
- Service worker for offline support
- Camera QR scanner via getUserMedia

no_std fixes:
- quality.rs test alloc import cleanup
- Cargo.toml feature gate for qr encoder

https://claude.ai/code/session_01RnwD4x5cbpB7FPvoyYQz8G
2026-02-15 18:37:19 +00:00

77 lines
2.1 KiB
JavaScript

/**
* RVF Seed Decoder - Service Worker
*
* Cache-first strategy for static assets and the WASM binary.
* Falls back to network on cache miss, then caches the response.
*/
const CACHE_NAME = 'rvf-pwa-v1';
const STATIC_ASSETS = [
'./',
'./index.html',
'./app.js',
'./style.css',
'./manifest.json',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => {
return cache.addAll(STATIC_ASSETS);
})
);
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil(
caches.keys().then((names) => {
return Promise.all(
names
.filter((name) => name !== CACHE_NAME)
.map((name) => caches.delete(name))
);
})
);
self.clients.claim();
});
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
// Cache-first for same-origin static assets and WASM binaries
if (url.origin === self.location.origin) {
event.respondWith(
caches.open(CACHE_NAME).then((cache) => {
return cache.match(event.request).then((cached) => {
if (cached) {
return cached;
}
return fetch(event.request).then((response) => {
// Cache successful GET responses
if (response.ok && event.request.method === 'GET') {
const isWasm = url.pathname.endsWith('.wasm');
const isStatic =
url.pathname.endsWith('.html') ||
url.pathname.endsWith('.js') ||
url.pathname.endsWith('.css') ||
url.pathname.endsWith('.json');
if (isWasm || isStatic) {
cache.put(event.request, response.clone());
}
}
return response;
});
});
}).catch(() => {
// Offline fallback: return cached index for navigation requests
if (event.request.mode === 'navigate') {
return caches.match('./index.html');
}
return new Response('Offline', { status: 503 });
})
);
}
});