first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
# `batch.ts`
## Purpose
Implements `BatchTransport` — the HTTP transport used by `AnalyticsTracker` for the steady-state, non-unload flush path. Every call posts a JSON-encoded `{ events }` body to the configured endpoint, wrapped in an `AbortController` whose timeout is armed via the injected `TimersLike`. The transport short-circuits on `4xx` responses (no retry), retries with exponential backoff on `5xx` / network errors, and translates any post-abort failure into a typed `AnalyticsBrowserError(TRANSPORT_TIMEOUT)` so callers can discriminate timeouts from other transport failures.
## Features
- `AbortController` + `timeoutMs` on every fetch — guaranteed bounded latency.
- `4xx` short-circuit — no retry storm on stable client errors.
- Exponential backoff on `5xx` / network — `retryDelay * 2 ** attempt`.
- Injectable `fetchFn` and `timers` — every browser-API touch is reachable from tests without DOM globals.
- Idempotent `shutdown()` — future `send()` calls are no-ops returning `true`.
## Usage: Basic
```ts
import { BatchTransport } from "@suaveplan/analytics-browser/transport";
const transport = new BatchTransport({ endpoint: "/api/analytics" });
await transport.send(events);
```
## Usage: Advanced
```ts
import { BatchTransport } from "@suaveplan/analytics-browser/transport";
const transport = new BatchTransport({
endpoint: "https://collector.example.com/v1/events",
maxRetries: 5,
retryDelay: 500,
timeoutMs: 5_000,
fetchFn: async (url, init) => {
const req = new Request(url, init);
return fetch(req, { credentials: "include" });
},
});
```
## API Reference
- `class BatchTransport implements Transport`
- `new BatchTransport(options)` — `options: BatchTransportOptions { endpoint, maxRetries?, retryDelay?, timeoutMs?, fetchFn?, timers? }`. Throws `AnalyticsBrowserError(INVALID_OPTIONS)` when `endpoint` is empty.
- `send(events) → Promise<boolean>` — `true` on success, `false` on `4xx` or exhausted retries. Throws `AnalyticsBrowserError(TRANSPORT_TIMEOUT)` when the fetch is aborted by the timeout.
- `shutdown() → Promise<void>` — disables further sends.
- `isActive() → boolean`.
## Implementation Notes
The timeout `setTimer` callback aborts the controller with an `AnalyticsBrowserError(TRANSPORT_TIMEOUT)` as the reason. If the host's `fetch` rejects with that exact error, the transport rethrows it as-is. If the fetch rejects with a different error after the signal has aborted (e.g. a `DOMException` synthesised by the runtime), the transport still maps the failure to `TRANSPORT_TIMEOUT` because the signal's aborted state is the authoritative reason. Otherwise the failure is treated as a generic retryable network error and the loop continues until `maxRetries` is exhausted.