PWA v6.0.0 - Synchronicity - Technical Documentation
| Document type | Internal reference |
|---|---|
| Applies to | service-worker.js 6.0.0, register-pwa.js, /offline-catalog, /offline.html |
| Entity | Clickerwayne Zelle Solutions Inc |
| Site | https://www.wholesaledito.store |
| Last reviewed | September 13, 2026 |
Overview
Wholesale Dito Store is a Progressive Web App. It runs in the browser, installs to a device home screen or app list, and works offline after the first visit. The app is built on three components:
- A service worker that intercepts network requests and serves cached responses when the network is unavailable.
- A registration script that installs the service worker and detects device capabilities.
- A catalog sync system that keeps product data available offline.
The service worker is version 6.0.0. It uses a config-driven design. Every tunable value lives in a single CONFIG object at the top of the file. Cache names are derived from the application name and version, so bumping one constant invalidates all old caches.
The service worker does not use generative AI. It does not call external AI services. All caching, syncing, and fallback behavior is deterministic code.
What the PWA does
When a user visits the site
- The browser loads the page.
register-pwa.jsruns and registers the service worker.- The service worker installs, precaches critical assets, and takes control of the page.
- The page syncs the product catalog into IndexedDB in the background.
When a user returns
- The service worker serves cached pages instantly.
- It revalidates them in the background.
- If the network is available, the cache updates.
- If the network is not available, the cached version is used.
When a user installs the app
- The app launches in standalone mode with no browser chrome.
- All service worker features remain active.
- Offline behavior is identical to the browser tab.
When a user goes offline
- Cached pages load normally.
- Product pages fall back to catalog data stored in IndexedDB.
- Uncached pages fall back to
/offline.html. - The user can retry the connection or navigate back to the last visited page.
What the PWA proves and what it does not
What it proves
- The service worker is registered and active.
- Cached assets are available offline.
- Product data is available from IndexedDB when the network is unavailable.
- The app can be installed to a device home screen.
- The app runs without generative AI on the client side.
What it does not prove
- That cached content is identical to the live server content at all times. Cached data ages. Revalidation keeps it fresh, but there is always a window between server updates and cache updates.
- That offline product data matches the current database. Catalog sync runs on a schedule. Between syncs, the offline catalog may be stale.
- That the service worker has never failed. Errors are logged to IndexedDB and can be inspected in DevTools.
Architecture
The service worker has six layers:
| Layer | Responsibility |
|---|---|
| Configuration | All tunable values in a single CONFIG object |
| Storage | Three IndexedDB databases, three Cache Storage buckets |
| State | Telemetry, sync state, circuit breaker state, network heuristics |
| Routing | Ordered route table, first match wins |
| Handlers | Product, navigation, API, image, static |
| Lifecycle | Install, activate, fetch, sync, message, push, notificationclick |
The separation is intentional. Each handler is independent. Shared helpers are centralized. No handler talks directly to another handler.
Storage locations
Cache Storage
| Cache name | Purpose |
|---|---|
wholesaleditostore-v6.0.0 | Precache of critical assets |
wholesaleditostore-runtime-v6.0.0 | Navigation, API, static, image runtime cache |
wholesaleditostore-product-html-v6.0.0 | Cached product HTML pages |
IndexedDB
| Database | Stores |
|---|---|
wholesaleditostore-api-v6.0.0 | responses - cached API responses |
wholesaleditostore-catalog-v6.0.0 | products - offline product catalog |
wholesaleditostore-meta-v6.0.0 | meta, state, cacheTimestamps, errorLogs |
Configuration constants
The CONFIG object controls:
- Application identity (name, title, currency)
- URL routing patterns (product path, API path, RSS path)
- Endpoint URLs (catalog, telemetry)
- Precache asset arrays
- Storage limits (max images, max catalog products, quota threshold)
- Cache durations (max age, stale age, revalidate age)
- Network timeouts
- Circuit breaker thresholds
- Batch sizes
- Sync limits
Changing any value takes effect on the next service worker install. Bumping SW_VERSION forces a new install.
Caching strategy by route
| Route | Strategy | Cache | Fallback |
|---|---|---|---|
RSS (/rss/*) | Network only | None | Browser error |
Product (/product-variant/*) | Cache first, network fallback | product-html | Catalog DB, then /offline.html |
| Navigation (any HTML page) | Stale while revalidate | runtime | /offline.html |
API (/api/*) | Network first, cache fallback | runtime + API DB | { offline: true } JSON |
Image (.webp, .avif, .png, .jpg, .svg) | Cache first, revalidate | runtime | Inline SVG placeholder |
Analytics (/analytics/*) | Network only | None | Silent failure |
| Static (everything else) | Cache first, stale while revalidate | runtime | Empty 503 |
Each route has a specific reason for its strategy. Product pages need offline access to catalog data. Navigation needs to serve fast but stay fresh. API endpoints should prefer fresh data but fall back gracefully.
Offline behavior
When the user is online
The service worker serves cached content first, then revalidates in the background. The user sees fast page loads. The cache updates silently.
When the user goes offline
Cached navigation pages load from cache. The user sees the page as it was when last visited.
Cached product pages load from the product-html cache if the page was visited before, or from the catalog DB if it was not.
Uncached pages fall back to /offline.html. This page shows:
- A clear message that the user is offline.
- A "Back to Last Page" button that returns to the last visited page in the same session.
- A "Retry Connection" button that reloads the current page.
Images that are not cached render as an inline SVG placeholder. No broken image icons.
API calls that fail return a JSON response with { offline: true }. The calling code can detect this and show the appropriate message.
Storing the last visited page
The last visited page URL is stored in sessionStorage on every page load. This is done by a global script that runs on all pages. It does not run on the offline page itself.
When the user clicks "Back to Last Page", the offline page reads sessionStorage.lastVisitedPage and navigates to it. If the stored value is missing or invalid, the page falls back to browser history, then to the home page.
Catalog sync
The offline catalog is synced from /offline-catalog. This endpoint returns JSON in the shape:
{
"products": [ ... ],
"synced_at": "2026-09-13T00:00:00+08:00",
"version": "6.0.0",
"total_count": 2000
} Each product has these fields:
| Field | Type | Used for |
|---|---|---|
id | integer | Fallback for image URL |
uri | string | IndexedDB key, image URL slug |
slug | string | Image URL template |
name | string | Page title, heading |
regular_price | number | Price display |
stock_quantity | integer | Stock badge |
size | string | Display |
packaging | integer | Display |
The service worker reads the response, filters products by slug validity, and writes them to IndexedDB in batches of 20. After the write, it precaches the first 20 product HTML pages in batches of 3.
Sync triggers
Catalog sync runs when:
- The user first navigates to a page (once per service worker lifetime).
- The browser fires the
syncevent with the tagcatalog-sync. - A client sends the
SYNC_CATALOGmessage. - The
periodicsyncevent fires with the tagmaintenance(supported browsers only).
Sync rate limits
- Maximum 3 attempts per 5 minutes.
- Minimum 10 minutes between successful syncs.
- If 3 consecutive failures occur, back off for 30 minutes.
- If a sync appears stuck for more than 5 minutes, force reset.
Circuit breakers
The service worker uses circuit breakers to prevent hammering endpoints that are failing. Two circuits are tracked:
api-fetchfor API requestsproduct-fetchfor product page requests
Each circuit has three states:
| State | Behavior |
|---|---|
| CLOSED | Normal. Requests pass through. |
| OPEN | Failures exceeded threshold. Requests fail fast. |
| HALF_OPEN | After reset window, one request is allowed through. Success returns to CLOSED. Failure returns to OPEN. |
Thresholds:
- Failure threshold: 5 consecutive failures
- Reset window: 30 seconds
Circuit state is persisted to IndexedDB so it survives service worker restarts.
Error logging
Errors are logged in three tiers:
- IndexedDB (
metadatabase,errorLogsstore). Primary. Persists across page loads. - postMessage relay. If IndexedDB is unavailable, errors are sent to all connected clients via
postMessage. - Console. Last resort. Only if both above fail.
Each error entry contains:
idunique identifiercontextwhere the error occurredmessageerror messagestackstack tracemetadataadditional contexttimestampwhen it happenedswVersionservice worker versionuserAgentbrowser identifier
Errors can be inspected in DevTools under Application, then IndexedDB, then wholesaleditostore-meta-v6.0.0, then errorLogs.
Errors are also flushed to /api/telemetry/errors when the endpoint is available. The flush runs during maintenance and on the flush-errors sync event.
Network heuristics
The service worker tracks network quality using two sources:
- Browser Network Information API (
navigator.connection). ProvidessaveData,effectiveType,rtt,downlink. - Empirical heuristics. Records fetch latencies and success/failure counts over a 30-second window.
When the network is slow, the API handler returns cached data even if it is stale. This prevents users on slow connections from waiting for a fetch that will time out.
Slowness is determined by:
saveDataenabledeffectiveTypeisslow-2gor2grttgreater than 1000 msdownlinkless than 0.5 Mbps- More than 50% fetch failures in the last 30 seconds
- Average successful latency greater than 2000 ms
Multi-tab safety
Two mechanisms prevent issues when the user opens the app in multiple tabs:
- Web Locks API. Used during database initialization. Only one tab can initialize the database at a time.
onversionchangehandler. Fires when another tab opens a newer version of the database. The current tab closes its connection and resets the cached instance.
Both are required for correct operation when the service worker version changes while the user has multiple tabs open.
Feature detection
register-pwa.js detects these capabilities on load:
| Feature | Purpose |
|---|---|
| File System Access | Open and save files (used for CSV uploads) |
| Contact Picker | Not available on desktop |
| Web Share Target | Not configured |
| Badging | Set numeric badge on the app icon |
| Idle Detection | Detect when the user is away |
| Wake Lock | Prevent screen sleep during long forms |
| Window Controls Overlay | Custom title bar for installed PWA |
Results are logged to the console. Detection failures are non-blocking. The app works with or without any feature.
Installation
The app can be installed to a device home screen or app list. The install flow:
- User visits the site.
- Browser shows an install prompt or the user selects "Install" from the browser menu.
- The app installs and adds an icon to the home screen or app list.
- The next time the user launches the app, it opens in standalone mode.
Standalone mode removes browser chrome and gives the app a full-screen window.
Verifying that the PWA is working
In the browser console
Look for these log lines:
Service Worker v6.0.0 initialized - Wholesale Dito
Running in browser mode
Network: online
Periodic sync not supported
Advanced PWA features detected: {...}
Wholesale Dito Store PWA Registration Script loaded successfully
Background sync registered
ServiceWorker registered successfully: {scope: '...', state: 'active'}
ServiceWorker active If any line is missing, the corresponding component did not run.
In DevTools
Application, Service Workers:
- Status should show "activated and is running"
- Scope should be
https://www.wholesaledito.store/
Application, Cache Storage:
- Three caches should be present:
wholesaleditostore-v6.0.0,wholesaleditostore-runtime-v6.0.0,wholesaleditostore-product-html-v6.0.0 - Each cache should have entries after the first visit
Application, IndexedDB:
- Three databases should be present:
wholesaleditostore-api-v6.0.0,wholesaleditostore-catalog-v6.0.0,wholesaleditostore-meta-v6.0.0 - The catalog database should have products after the first sync
- The meta database should have telemetry and sync state entries
From the command line
# Confirm the service worker file is served
curl -sI https://www.wholesaledito.store/service-worker.js | head -1
# Confirm the catalog endpoint returns JSON
curl -s https://www.wholesaledito.store/offline-catalog | jq '.total_count'
# Confirm the offline page exists
curl -sI https://www.wholesaledito.store/offline.html | head -1
# Confirm the registration script is present on a page
curl -s https://www.wholesaledito.store/ | grep -o 'register-pwa.js' Updating the service worker
To update:
- Edit the service worker file.
- Bump
SW_VERSIONin theCONFIGobject. - Save and deploy.
- On the next visit, the browser detects the new version and installs it in the background.
- The new version activates after all open tabs close or after a
SKIP_WAITINGmessage.
Bumping the version causes the browser to:
- Delete old caches (those with the previous version in the name).
- Run the install event to precache.
- Run the activate event to take control.
The activation is usually silent to the user. They see the site as normal. The new service worker is now active.
Common issues
The service worker is not registering
Symptom: No "ServiceWorker registered successfully" in the console.
Possible causes:
- The page does not include
register-pwa.js. - The service worker file is not served with the correct MIME type.
- The scope in the manifest does not cover the current page.
- The browser is in private mode and does not allow service workers.
Fix: Check that register-pwa.js is present on the page. Fetch /service-worker.js directly to confirm it is served. Check DevTools, Application, Service Workers for error messages.
Cached content is stale
Symptom: The user sees an old version of a page.
Possible causes:
- The cache has not revalidated yet.
- The service worker has not been updated.
- A specific route is using a cache-first strategy for content that changes frequently.
Fix: Check the cache duration settings. Force an update by bumping the service worker version. Reload with cache disabled to see the live server version.
Offline product pages show the fallback
Symptom: Visiting a product page offline shows /offline.html instead of the product.
Possible causes:
- The catalog sync has not run or has failed.
- The product slug is not in the catalog database.
- The product HTML is not in the
product-htmlcache.
Fix: Trigger a sync manually. Check the catalog database in DevTools. Confirm the product's uri field is in the database.
Error logs are not flushing
Symptom: Errors accumulate in the errorLogs store.
Possible causes:
- The
/api/telemetry/errorsendpoint is not available. - The endpoint returns an error.
Fix: Either build the telemetry endpoint or remove the flush call. Without a working endpoint, logs will grow indefinitely.
What this documentation does not cover
- How the site is built (build process, deployment, server configuration)
- How the provenance engine works (separate documentation)
- How the database schema is structured (separate documentation)
- How the article pages are constructed (separate documentation)
- How the price manifests are signed (separate documentation)
This document covers the PWA only: the service worker, the registration script, the offline catalog, and the offline page.
Revision history
| Date | Change |
|---|---|
| September 13, 2026 | Initial documentation for v6.0.0 |
Contact
For questions about this documentation, contact the developer at Clickerwayne Zelle Solutions Inc.