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:

  1. A service worker that intercepts network requests and serves cached responses when the network is unavailable.
  2. A registration script that installs the service worker and detects device capabilities.
  3. 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

  1. The browser loads the page.
  2. register-pwa.js runs and registers the service worker.
  3. The service worker installs, precaches critical assets, and takes control of the page.
  4. The page syncs the product catalog into IndexedDB in the background.

When a user returns

  1. The service worker serves cached pages instantly.
  2. It revalidates them in the background.
  3. If the network is available, the cache updates.
  4. If the network is not available, the cached version is used.

When a user installs the app

  1. The app launches in standalone mode with no browser chrome.
  2. All service worker features remain active.
  3. Offline behavior is identical to the browser tab.

When a user goes offline

  1. Cached pages load normally.
  2. Product pages fall back to catalog data stored in IndexedDB.
  3. Uncached pages fall back to /offline.html.
  4. 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 sync event with the tag catalog-sync.
  • A client sends the SYNC_CATALOG message.
  • The periodicsync event fires with the tag maintenance (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-fetch for API requests
  • product-fetch for 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:

  1. IndexedDB (meta database, errorLogs store). Primary. Persists across page loads.
  2. postMessage relay. If IndexedDB is unavailable, errors are sent to all connected clients via postMessage.
  3. Console. Last resort. Only if both above fail.

Each error entry contains:

  • id unique identifier
  • context where the error occurred
  • message error message
  • stack stack trace
  • metadata additional context
  • timestamp when it happened
  • swVersion service worker version
  • userAgent browser 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:

  1. Browser Network Information API (navigator.connection). Provides saveData, effectiveType, rtt, downlink.
  2. 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:

  • saveData enabled
  • effectiveType is slow-2g or 2g
  • rtt greater than 1000 ms
  • downlink less 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:

  1. Web Locks API. Used during database initialization. Only one tab can initialize the database at a time.
  2. onversionchange handler. 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:

  1. User visits the site.
  2. Browser shows an install prompt or the user selects "Install" from the browser menu.
  3. The app installs and adds an icon to the home screen or app list.
  4. 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:

  1. Edit the service worker file.
  2. Bump SW_VERSION in the CONFIG object.
  3. Save and deploy.
  4. On the next visit, the browser detects the new version and installs it in the background.
  5. The new version activates after all open tabs close or after a SKIP_WAITING message.

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-html cache.

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/errors endpoint 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.