2.7 KiB
beacon.ts
Purpose
Implements BeaconTransport — the page-unload-safe transport used by AnalyticsTracker.shutdown() and any consumer wiring a visibilitychange → hidden flush. Browsers guarantee that navigator.sendBeacon will complete the request even during unload, which makes it the only reliable way to ship a last batch before navigation. When sendBeacon returns false (body too large, host rejected the beacon, navigator unavailable), the transport optionally falls back to fetch with keepalive: true or throws a typed AnalyticsBrowserError(BEACON_REJECTED).
Features
navigator.sendBeaconfirst,fetch keepalivefallback.- Injectable
sendBeaconFnandfetchFnso happy-dom and Node test envs can drive both paths. - Graceful degradation — missing
navigatoror missingsendBeaconreturnsfalse, letting the fallback or the typed error path take over. fallbackToFetchdefaults totrueso the steady-state behaviour matches what production code wants.
Usage: Basic
import { BeaconTransport } from "@suaveplan/analytics-browser/transport";
const beacon = new BeaconTransport({ endpoint: "/api/analytics" });
window.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
void tracker.flush();
}
});
Usage: Advanced
import { BeaconTransport } from "@suaveplan/analytics-browser/transport";
const beacon = new BeaconTransport({
endpoint: "/api/analytics",
fallbackToFetch: false, // strict — surface BEACON_REJECTED to the caller
sendBeaconFn: (url, body) => navigator.sendBeacon(url, body),
});
API Reference
class BeaconTransport implements Transportnew BeaconTransport(options)—options: BeaconTransportOptions { endpoint, fallbackToFetch?, sendBeaconFn?, fetchFn? }. ThrowsAnalyticsBrowserError(INVALID_OPTIONS)whenendpointis empty.send(events) → Promise<boolean>—trueon beacon success or200fallback fetch. ThrowsAnalyticsBrowserError(BEACON_REJECTED)whensendBeaconFnreturnsfalseandfallbackToFetch === false.shutdown() → Promise<void>— disables future sends.isActive() → boolean.
Implementation Notes
sendBeacon is synchronous and returns a boolean that signals whether the request was queued, not whether the server received it. The transport treats true as success because the browser owns delivery from that point on — there is no observable response to inspect during unload. The fallback fetch sets keepalive: true so the same unload-safety guarantee applies; keepalive is supported in every browser that ships sendBeacon, which keeps the fallback meaningful.