first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user