Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
# `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.sendBeacon` first, `fetch keepalive` fallback.
- Injectable `sendBeaconFn` and `fetchFn` so happy-dom and Node test envs can drive both paths.
- Graceful degradation — missing `navigator` or missing `sendBeacon` returns `false`, letting the fallback or the typed error path take over.
- `fallbackToFetch` defaults to `true` so the steady-state behaviour matches what production code wants.
## Usage: Basic
```ts
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
```ts
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 Transport`
- `new BeaconTransport(options)` — `options: BeaconTransportOptions { endpoint, fallbackToFetch?, sendBeaconFn?, fetchFn? }`. Throws `AnalyticsBrowserError(INVALID_OPTIONS)` when `endpoint` is empty.
- `send(events) → Promise<boolean>` — `true` on beacon success or `200` fallback fetch. Throws `AnalyticsBrowserError(BEACON_REJECTED)` when `sendBeaconFn` returns `false` and `fallbackToFetch === 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.