2.6 KiB
sync-queue/background-sync.ts
Purpose
Bridges the standard W3C Background Sync API (SyncManager.register(tag))
into the @suaveplan/pwa package. Where the runtime supports it (every recent
Chromium-based browser), the bridge registers a tag whose corresponding
sync event the consumer's service worker handles. Where it does not (Firefox
and Safari), the bridge falls back to a visibilitychange listener that
invokes the supplied flush callback every time the tab transitions to the
visible state — a close-enough approximation of "do work when the user comes
back".
Features
- Detects native support by inspecting the supplied registration for
registration.syncrather thannavigator.serviceWorker, so the bridge also works in worker contexts. - Visibility-change fallback fires exactly once per transition to
visible(no spurious second invocations when the document is already visible). unregister()removes the visibility listener; native registrations have no programmatic deregistration in the W3C spec, so the call is a no-op for that path.taggetter exposes the currently-registered tag for diagnostic surfaces.
Usage: Basic
import { BackgroundSyncBridge } from "@suaveplan/pwa";
const bridge = new BackgroundSyncBridge({ registration, document });
await bridge.register("retry-pending", () => queue.process());
Usage: Advanced
Inspect the returned mode to differ telemetry per platform:
import { BackgroundSyncBridge } from "@suaveplan/pwa";
const bridge = new BackgroundSyncBridge({ registration, document });
const mode = await bridge.register("retry-pending", flushQueue);
metrics.bsyncRegistered.add(1, { mode });
API Reference
new BackgroundSyncBridge(options: BackgroundSyncBridgeOptions).register(tag, flush): Promise<"native" | "visibility">.unregister(): void.tag: string | null.SyncManagerLike,SyncCapableRegistration,VisibilityDocumentLike— injectable seams.
Implementation Notes
The visibility-fallback listener compares the current visibilityState
against a private _lastState so it only fires when the document transitions
into the visible state from any other state, not on the no-op event that
fires when a tab is opened in the foreground. This keeps the flush callback's
invocation count proportional to actual user activity rather than to the
number of visibilitychange events the browser happens to dispatch. The
register() body intentionally requires either a sync-capable registration
or a document seam — passing neither is a programming error and surfaces as a
thrown Error rather than silently no-opping.