# `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.sync` rather than `navigator.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. - `tag` getter exposes the currently-registered tag for diagnostic surfaces. ## Usage: Basic ```ts 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: ```ts 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.