@eventra_dev/eventra-sdk 1.2.2 → 1.2.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -5
- package/dist/index.cjs +29 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.mjs +29 -24
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<a href="https://www.npmjs.com/package/@eventra_dev/eventra-sdk"><img alt="npm version" src="https://img.shields.io/npm/v/@eventra_dev/eventra-sdk.svg?style=flat-square&color=blue"></a>
|
|
9
9
|
<a href="https://www.npmjs.com/package/@eventra_dev/eventra-sdk"><img alt="npm downloads" src="https://img.shields.io/npm/dm/@eventra_dev/eventra-sdk.svg?style=flat-square&color=blue"></a>
|
|
10
10
|
<a href="https://github.com/and-1991/eventra-sdk/actions/workflows/test.yml"><img alt="tests" src="https://img.shields.io/github/actions/workflow/status/and-1991/eventra-sdk/test.yml?branch=main&label=tests&style=flat-square&logo=vitest&logoColor=white"></a>
|
|
11
|
-
<img alt="tests passing" src="https://img.shields.io/badge/tests-
|
|
11
|
+
<img alt="tests passing" src="https://img.shields.io/badge/tests-44%20passing-brightgreen?style=flat-square&logo=vitest&logoColor=white">
|
|
12
12
|
<img alt="test suites" src="https://img.shields.io/badge/suites-8-brightgreen?style=flat-square">
|
|
13
13
|
<img alt="node" src="https://img.shields.io/node/v/@eventra_dev/eventra-sdk?style=flat-square&color=darkgreen&logo=node.js&logoColor=white">
|
|
14
14
|
<a href="https://www.typescriptlang.org/"><img alt="TypeScript" src="https://img.shields.io/badge/TypeScript-ready-blue?style=flat-square&logo=typescript&logoColor=white"></a>
|
|
@@ -121,7 +121,7 @@ Minimal:
|
|
|
121
121
|
tracker.track("app.loaded");
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
-
Event names are trimmed to 64 characters, `userId` to 120 (API limits).
|
|
124
|
+
Event names are trimmed to 64 characters, `userId` to 120 (API limits). `track()` never throws: an empty name, or properties that exceed the depth/size limits, are dropped silently and reported via `onEventsDropped` instead.
|
|
125
125
|
|
|
126
126
|
---
|
|
127
127
|
|
|
@@ -218,6 +218,17 @@ const tracker = new Eventra({
|
|
|
218
218
|
});
|
|
219
219
|
```
|
|
220
220
|
|
|
221
|
+
**Privacy note:** persistence stores the pending queue — including any `userId`/`properties` you pass to `track()` — as plaintext JSON in `localStorage` so it survives reloads. If that's a concern (e.g. properties may carry PII on a shared/public device), disable it:
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
const tracker = new Eventra({
|
|
225
|
+
apiKey: "...",
|
|
226
|
+
persistQueue: false,
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
With `persistQueue: false`, nothing is written to `localStorage`; the queue lives in memory only and is lost if the page closes before delivery (live cross-tab dedup via `BroadcastChannel` still works, since that's never written to disk).
|
|
231
|
+
|
|
221
232
|
---
|
|
222
233
|
|
|
223
234
|
### Node.js
|
|
@@ -310,6 +321,7 @@ const tracker = new Eventra({
|
|
|
310
321
|
| onEventsDropped | Callback when queue is full |
|
|
311
322
|
| onDeliveryFailed | Callback on permanent ingest errors (4xx except 429) |
|
|
312
323
|
| multiTabMode | `"independent"` (default) or `"leader"` (browser only) |
|
|
324
|
+
| persistQueue | Persist the queue to `localStorage` in the browser (browser only, default: `true`) — `false` keeps it in-memory only, avoiding writing `userId`/`properties` to disk |
|
|
313
325
|
|
|
314
326
|
---
|
|
315
327
|
|
|
@@ -349,11 +361,11 @@ Eventra SDK includes:
|
|
|
349
361
|
- Circuit breaker with half-open recovery
|
|
350
362
|
- Queue-based delivery (all runtimes)
|
|
351
363
|
- Safe dequeue (events removed only after successful ingest)
|
|
352
|
-
- Queue persistence (browser) with merge + cross-tab sync
|
|
364
|
+
- Queue persistence (browser) with merge + cross-tab sync — opt out via `persistQueue: false` if `localStorage` isn't acceptable for your data
|
|
353
365
|
- Multi-tab leader election with re-election
|
|
354
366
|
- `fetch` + `keepalive` on tab close (with `x-api-key`, size-checked)
|
|
355
367
|
- `pagehide` + `visibilitychange` flush hooks
|
|
356
|
-
- Property validation at `track()` (depth + size)
|
|
368
|
+
- Property validation at `track()` (depth + size) — fails soft via `onEventsDropped`, `track()` never throws
|
|
357
369
|
- Payload byte limits per batch
|
|
358
370
|
- Permanent error handling via `onDeliveryFailed` (401, 422, etc.)
|
|
359
371
|
- Automatic requeue on network errors and 429 / 5xx
|
|
@@ -362,7 +374,7 @@ Eventra SDK includes:
|
|
|
362
374
|
|
|
363
375
|
## Test Coverage
|
|
364
376
|
|
|
365
|
-
|
|
377
|
+
44 vitest tests across 8 suites cover the entire delivery pipeline:
|
|
366
378
|
|
|
367
379
|
- `track()` validation — name length, userId truncation, properties depth/size, idempotency keys
|
|
368
380
|
- Batching — auto-flush at `maxBatchSize`, periodic flush via timer, queue overflow drop
|
|
@@ -380,6 +392,8 @@ pnpm --filter @eventra_dev/eventra-sdk test
|
|
|
380
392
|
pnpm --filter @eventra_dev/eventra-sdk test:coverage
|
|
381
393
|
```
|
|
382
394
|
|
|
395
|
+
> **Node version note:** the SDK itself supports Node 18+ (see `engines`), but `vitest@4` depends on `node:util`'s `styleText`, which requires Node **≥ 20.12** — running the test suite on an older Node 20.x patch (or Node 18) fails to start. CI runs the matrix on Node 20 and 22; locally, use the version pinned in the repo's `.nvmrc`.
|
|
396
|
+
|
|
383
397
|
---
|
|
384
398
|
|
|
385
399
|
## Event Format
|
package/dist/index.cjs
CHANGED
|
@@ -27,7 +27,7 @@ module.exports = __toCommonJS(index_exports);
|
|
|
27
27
|
// src/client.ts
|
|
28
28
|
var SDK_NAME = "@eventra_dev/eventra-sdk";
|
|
29
29
|
var DEFAULT_ENDPOINT = "https://api.eventra.dev/api/v1/ingest/batch";
|
|
30
|
-
var SDK_VERSION = "1.2.
|
|
30
|
+
var SDK_VERSION = "1.2.41";
|
|
31
31
|
var LEADER_KEY = "__eventra_leader__";
|
|
32
32
|
var LEADER_TTL_MS = 4e3;
|
|
33
33
|
var QUEUE_KEY = "__eventra_q__";
|
|
@@ -71,15 +71,10 @@ function truncate(value, max) {
|
|
|
71
71
|
if (value === void 0) return void 0;
|
|
72
72
|
return value.length > max ? value.slice(0, max) : value;
|
|
73
73
|
}
|
|
74
|
-
function
|
|
75
|
-
const trimmed = name.trim();
|
|
76
|
-
if (!trimmed)
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
if (trimmed.length > MAX_EVENT_NAME) {
|
|
80
|
-
throw new Error(`Eventra: event name exceeds max length (${MAX_EVENT_NAME})`);
|
|
81
|
-
}
|
|
82
|
-
return trimmed;
|
|
74
|
+
function normalizeEventName(name) {
|
|
75
|
+
const trimmed = (name ?? "").trim();
|
|
76
|
+
if (!trimmed) return null;
|
|
77
|
+
return trimmed.length > MAX_EVENT_NAME ? trimmed.slice(0, MAX_EVENT_NAME) : trimmed;
|
|
83
78
|
}
|
|
84
79
|
function backoffMs(attempt, base) {
|
|
85
80
|
const exp = Math.min(DEFAULT_MAX_RETRY_DELAY_MS, base * 2 ** attempt);
|
|
@@ -321,13 +316,16 @@ var Eventra = class {
|
|
|
321
316
|
this.flushInterval = options.flushInterval ?? 2e3;
|
|
322
317
|
this.retries = options.maxRetries ?? 3;
|
|
323
318
|
this.retryDelay = options.retryBaseDelayMs ?? 300;
|
|
319
|
+
this.persistQueue = options.persistQueue ?? true;
|
|
324
320
|
this.sdkInfo = {
|
|
325
321
|
name: SDK_NAME,
|
|
326
322
|
version: SDK_VERSION,
|
|
327
323
|
runtime: this.runtime
|
|
328
324
|
};
|
|
329
325
|
if (this.runtime === "browser") {
|
|
330
|
-
this.
|
|
326
|
+
if (this.persistQueue) {
|
|
327
|
+
this.loadAndMergeQueue();
|
|
328
|
+
}
|
|
331
329
|
this.setupQueueSync();
|
|
332
330
|
}
|
|
333
331
|
if (this.runtime === "browser" && options.multiTabMode === "leader") {
|
|
@@ -347,14 +345,17 @@ var Eventra = class {
|
|
|
347
345
|
}
|
|
348
346
|
track(name, options) {
|
|
349
347
|
if (this.destroyed || this.shuttingDown) return;
|
|
350
|
-
|
|
348
|
+
const eventName = normalizeEventName(name);
|
|
349
|
+
if (eventName === null) {
|
|
350
|
+
this.options.onEventsDropped?.(1);
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
351
353
|
let properties;
|
|
352
354
|
try {
|
|
353
|
-
eventName = validateEventName(name);
|
|
354
355
|
properties = validateProperties(options?.properties ?? {});
|
|
355
|
-
} catch
|
|
356
|
-
|
|
357
|
-
|
|
356
|
+
} catch {
|
|
357
|
+
this.options.onEventsDropped?.(1);
|
|
358
|
+
return;
|
|
358
359
|
}
|
|
359
360
|
if (this.queue.length >= this.maxQueue) {
|
|
360
361
|
this.options.onEventsDropped?.(1);
|
|
@@ -554,13 +555,17 @@ var Eventra = class {
|
|
|
554
555
|
this.trimQueue();
|
|
555
556
|
}
|
|
556
557
|
syncQueue() {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
558
|
+
if (this.persistQueue) {
|
|
559
|
+
const remote = this.storage.get(QUEUE_KEY);
|
|
560
|
+
const merged = mergeQueues(
|
|
561
|
+
Array.isArray(remote) ? remote : [],
|
|
562
|
+
this.queue
|
|
563
|
+
);
|
|
564
|
+
this.queue = this.trimQueue(merged);
|
|
565
|
+
this.storage.set(QUEUE_KEY, this.queue);
|
|
566
|
+
} else {
|
|
567
|
+
this.queue = this.trimQueue();
|
|
568
|
+
}
|
|
564
569
|
this.broadcastQueue();
|
|
565
570
|
}
|
|
566
571
|
trimQueue(queue = this.queue) {
|
|
@@ -587,7 +592,7 @@ var Eventra = class {
|
|
|
587
592
|
this.queue = this.trimQueue();
|
|
588
593
|
};
|
|
589
594
|
}
|
|
590
|
-
if (typeof window !== "undefined") {
|
|
595
|
+
if (this.persistQueue && typeof window !== "undefined") {
|
|
591
596
|
this.onStorageQueue = (e) => {
|
|
592
597
|
if (e.key !== QUEUE_KEY) return;
|
|
593
598
|
this.loadAndMergeQueue();
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export * from \"./client\";\nexport * from \"./types\";\n","import { TrackerOptions, TrackEvent, SdkInfo, TrackOptions } from \"./types\";\n\ndeclare const __SDK_VERSION__: string | undefined;\ndeclare const __EVENTRA_ENDPOINT__: string | undefined;\n\nconst SDK_NAME = \"@eventra_dev/eventra-sdk\";\nconst DEFAULT_ENDPOINT =\n (typeof __EVENTRA_ENDPOINT__ !== \"undefined\" && __EVENTRA_ENDPOINT__) ||\n \"https://api.eventra.dev/api/v1/ingest/batch\";\nconst SDK_VERSION =\n (typeof __SDK_VERSION__ !== \"undefined\" && __SDK_VERSION__) || \"0.0.0-dev\";\n\nconst LEADER_KEY = \"__eventra_leader__\";\nconst LEADER_TTL_MS = 4000;\nconst QUEUE_KEY = \"__eventra_q__\";\nconst CHANNEL_NAME = \"eventra-sdk\";\n\nconst MAX_EVENT_NAME = 64;\nconst MAX_USER_ID = 120;\nconst MAX_PROPERTIES_JSON_BYTES = 32_000;\nconst MAX_PROPERTIES_DEPTH = 8;\nconst DEFAULT_MAX_PAYLOAD_BYTES = 60_000;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst FETCH_TIMEOUT_MS = 5000;\nconst CIRCUIT_FAILURE_THRESHOLD = 5;\nconst CIRCUIT_COOLDOWN_MS = 5000;\n\nlet activeInstances = 0;\n\n// ================= RUNTIME =================\ntype Runtime = \"browser\" | \"node\" | \"edge\" | \"serverless\" | \"unknown\";\n\nfunction detectRuntime(): Runtime {\n const g = globalThis as typeof globalThis & {\n EdgeRuntime?: unknown;\n process?: { env?: Record<string, string>; versions?: { node?: string } };\n };\n\n if (typeof window !== \"undefined\" && !g.EdgeRuntime) return \"browser\";\n if (g.EdgeRuntime) return \"edge\";\n if (g.process?.env?.AWS_LAMBDA_FUNCTION_NAME) return \"serverless\";\n if (g.process?.versions?.node) return \"node\";\n\n return \"unknown\";\n}\n\n// ================= HELPERS =================\nfunction uuidV4(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n\n throw new Error(\"Eventra: crypto API unavailable — cannot generate idempotencyKey\");\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction truncate(value: string | undefined, max: number): string | undefined {\n if (value === undefined) return undefined;\n return value.length > max ? value.slice(0, max) : value;\n}\n\nfunction validateEventName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) {\n throw new Error(\"Eventra: event name is required\");\n }\n if (trimmed.length > MAX_EVENT_NAME) {\n throw new Error(`Eventra: event name exceeds max length (${MAX_EVENT_NAME})`);\n }\n return trimmed;\n}\n\nfunction backoffMs(attempt: number, base: number): number {\n const exp = Math.min(DEFAULT_MAX_RETRY_DELAY_MS, base * 2 ** attempt);\n const jitter = exp * (0.5 + Math.random() * 0.5);\n return jitter;\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== \"undefined\") {\n return new TextEncoder().encode(value).length;\n }\n const g = globalThis as { Buffer?: { byteLength: (s: string, enc: string) => number } };\n if (g.Buffer) {\n return g.Buffer.byteLength(value, \"utf8\");\n }\n return value.length;\n}\n\nfunction mergeQueues(...queues: TrackEvent[][]): TrackEvent[] {\n const seen = new Set<string>();\n const out: TrackEvent[] = [];\n\n for (const q of queues) {\n for (const e of q) {\n if (seen.has(e.idempotencyKey)) continue;\n seen.add(e.idempotencyKey);\n out.push(e);\n }\n }\n\n return out;\n}\n\nfunction validateProperties(\n properties: Record<string, unknown>,\n): Record<string, unknown> {\n const depth = (obj: unknown, level: number): void => {\n if (level > MAX_PROPERTIES_DEPTH) {\n throw new Error(`Eventra: properties exceed max depth (${MAX_PROPERTIES_DEPTH})`);\n }\n if (obj === null || typeof obj !== \"object\") return;\n if (Array.isArray(obj)) {\n for (const item of obj) depth(item, level + 1);\n return;\n }\n for (const value of Object.values(obj as Record<string, unknown>)) {\n depth(value, level + 1);\n }\n };\n\n depth(properties, 0);\n\n const json = JSON.stringify(properties);\n if (utf8ByteLength(json) > MAX_PROPERTIES_JSON_BYTES) {\n throw new Error(\n `Eventra: properties exceed max size (${MAX_PROPERTIES_JSON_BYTES} bytes)`,\n );\n }\n\n return properties;\n}\n\nfunction estimateEnvelopeBytes(\n events: TrackEvent[],\n sdk: SdkInfo,\n sentAt: string,\n): number {\n return utf8ByteLength(\n JSON.stringify({\n sentAt,\n sdk,\n events,\n }),\n );\n}\n\nclass RetryableDeliveryError extends Error {\n constructor(message = \"retryable delivery error\") {\n super(message);\n this.name = \"RetryableDeliveryError\";\n }\n}\n\n// ================= STORAGE =================\nclass Storage {\n private enabled = false;\n\n constructor() {\n try {\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(\"__t\", \"1\");\n localStorage.removeItem(\"__t\");\n this.enabled = true;\n }\n } catch {\n /* private mode / blocked storage */\n }\n }\n\n get(key: string): unknown {\n if (!this.enabled) return null;\n try {\n return JSON.parse(localStorage.getItem(key) || \"null\");\n } catch {\n return null;\n }\n }\n\n set(key: string, value: unknown) {\n if (!this.enabled) return;\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n /* quota exceeded */\n }\n }\n\n remove(key: string) {\n if (!this.enabled) return;\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n }\n}\n\n// ================= LEADER =================\nclass Leader {\n private tabId = uuidV4();\n private isLeader = false;\n private heartbeat?: ReturnType<typeof setInterval>;\n private election?: ReturnType<typeof setInterval>;\n private onStorage?: (e: StorageEvent) => void;\n\n constructor(private onLeadershipChange?: (isLeader: boolean) => void) {\n if (typeof localStorage === \"undefined\") {\n this.isLeader = true;\n return;\n }\n\n this.tick();\n\n this.election = setInterval(() => this.tick(), LEADER_TTL_MS / 2);\n\n if (typeof window !== \"undefined\") {\n this.onStorage = (e: StorageEvent) => {\n if (e.key === LEADER_KEY) this.tick();\n };\n window.addEventListener(\"storage\", this.onStorage);\n }\n }\n\n private tick() {\n const was = this.isLeader;\n\n if (this.isLeader) {\n if (!this.verifyLease()) {\n this.isLeader = false;\n } else {\n this.renew();\n }\n } else {\n this.isLeader = this.tryAcquire();\n if (this.isLeader) {\n if (!this.heartbeat) {\n this.heartbeat = setInterval(() => this.renew(), LEADER_TTL_MS / 2);\n }\n }\n }\n\n if (!this.isLeader && this.heartbeat) {\n clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n }\n\n if (was !== this.isLeader) {\n this.onLeadershipChange?.(this.isLeader);\n }\n }\n\n private tryAcquire(): boolean {\n try {\n const now = Date.now();\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) {\n this.renew();\n return true;\n }\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n if (now - parsed.ts > LEADER_TTL_MS) {\n this.renew();\n return true;\n }\n if (parsed.tabId === this.tabId) {\n this.renew();\n return true;\n }\n return false;\n } catch {\n return true;\n }\n }\n\n private verifyLease(): boolean {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) return false;\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n return (\n parsed.tabId === this.tabId && Date.now() - parsed.ts <= LEADER_TTL_MS\n );\n } catch {\n return false;\n }\n }\n\n private renew() {\n try {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: Date.now(), tabId: this.tabId }),\n );\n } catch {\n /* ignore */\n }\n }\n\n canSend() {\n return this.isLeader;\n }\n\n destroy() {\n if (this.election) clearInterval(this.election);\n if (this.heartbeat) clearInterval(this.heartbeat);\n if (this.onStorage && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorage);\n }\n if (this.isLeader && typeof localStorage !== \"undefined\") {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as { tabId: string };\n if (parsed.tabId === this.tabId) {\n localStorage.removeItem(LEADER_KEY);\n }\n }\n } catch {\n /* ignore */\n }\n }\n }\n}\n\n// ================= SDK =================\nexport class Eventra {\n private apiKey: string;\n private endpoint: string;\n private runtime: Runtime;\n private fetch: typeof fetch;\n\n private queue: TrackEvent[] = [];\n private sending: TrackEvent[] | null = null;\n private inFlight = false;\n private destroyed = false;\n private shuttingDown = false;\n\n private maxBatch: number;\n private maxQueue: number;\n private maxPayloadBytes: number;\n private flushInterval: number;\n private retries: number;\n private retryDelay: number;\n\n private timer?: ReturnType<typeof setInterval>;\n\n private storage = new Storage();\n private leader: Leader | null = null;\n private channel?: BroadcastChannel;\n private onStorageQueue?: (e: StorageEvent) => void;\n\n private failureCount = 0;\n private circuitOpenUntil = 0;\n private circuitHalfOpen = false;\n\n private sdkInfo: SdkInfo;\n private options: TrackerOptions;\n\n private exitHandlers: Array<() => void> = [];\n\n constructor(options: TrackerOptions) {\n if (!options.apiKey) throw new Error(\"Eventra: apiKey required\");\n\n activeInstances++;\n if (activeInstances > 1 && typeof console !== \"undefined\") {\n console.warn(\n \"Eventra: multiple SDK instances detected — duplicate timers and sends are possible\",\n );\n }\n\n this.options = options;\n this.apiKey = options.apiKey;\n this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;\n this.runtime = detectRuntime();\n\n this.fetch = options.fetchImpl ?? globalThis.fetch;\n if (!this.fetch) {\n throw new Error(\"Eventra: fetch not available — provide fetchImpl\");\n }\n\n this.maxBatch = options.maxBatchSize ?? 50;\n this.maxQueue = options.maxQueueSize ?? 10000;\n this.maxPayloadBytes = options.maxPayloadBytes ?? DEFAULT_MAX_PAYLOAD_BYTES;\n this.flushInterval = options.flushInterval ?? 2000;\n this.retries = options.maxRetries ?? 3;\n this.retryDelay = options.retryBaseDelayMs ?? 300;\n\n this.sdkInfo = {\n name: SDK_NAME,\n version: SDK_VERSION,\n runtime: this.runtime,\n };\n\n if (this.runtime === \"browser\") {\n this.loadAndMergeQueue();\n this.setupQueueSync();\n }\n\n if (this.runtime === \"browser\" && options.multiTabMode === \"leader\") {\n this.leader = new Leader((isLeader) => {\n if (isLeader) void this.flush();\n });\n }\n\n if (!options.disableTimer) {\n this.startTimer();\n }\n\n if (this.runtime === \"browser\") {\n this.setupBrowserExit();\n }\n\n if (options.autoFlushOnExit !== false && this.runtime !== \"browser\") {\n this.setupProcessExit();\n }\n }\n\n track(name: string, options?: TrackOptions) {\n if (this.destroyed || this.shuttingDown) return;\n\n let eventName: string;\n let properties: Record<string, unknown>;\n try {\n eventName = validateEventName(name);\n properties = validateProperties(options?.properties ?? {});\n } catch (err) {\n const message = err instanceof Error ? err.message : \"invalid event\";\n throw new Error(message);\n }\n\n if (this.queue.length >= this.maxQueue) {\n this.options.onEventsDropped?.(1);\n return;\n }\n\n const event: TrackEvent = {\n idempotencyKey: uuidV4(),\n name: eventName,\n userId: truncate(options?.userId, MAX_USER_ID),\n properties,\n timestamp: new Date().toISOString(),\n };\n\n this.queue.push(event);\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n\n if (this.queue.length >= this.maxBatch) {\n void this.flush();\n }\n\n if (this.runtime === \"serverless\") {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.inFlight || this.destroyed) return;\n if (!this.queue.length && !this.sending?.length) return;\n\n if (this.leader && !this.leader.canSend()) return;\n\n const now = Date.now();\n if (now < this.circuitOpenUntil) return;\n\n if (this.circuitOpenUntil > 0 && now >= this.circuitOpenUntil) {\n this.circuitHalfOpen = true;\n this.circuitOpenUntil = 0;\n }\n\n this.inFlight = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.buildBatch();\n if (!batch.length) break;\n\n this.sending = batch;\n\n try {\n await this.send(batch);\n this.removeDelivered(batch);\n this.failureCount = 0;\n this.circuitHalfOpen = false;\n } catch (err) {\n if (err instanceof RetryableDeliveryError) {\n this.failureCount++;\n if (this.circuitHalfOpen || this.failureCount >= CIRCUIT_FAILURE_THRESHOLD) {\n this.circuitOpenUntil = Date.now() + CIRCUIT_COOLDOWN_MS;\n this.circuitHalfOpen = false;\n }\n break;\n }\n break;\n } finally {\n this.sending = null;\n }\n\n if (this.circuitOpenUntil > Date.now()) break;\n }\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n } finally {\n this.inFlight = false;\n }\n }\n\n /** Flush pending events, then tear down timers and listeners */\n async shutdown(): Promise<void> {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n try {\n await this.flush();\n } finally {\n this.destroy();\n }\n }\n\n destroy() {\n if (this.destroyed) return;\n this.destroyed = true;\n activeInstances = Math.max(0, activeInstances - 1);\n\n if (this.timer) clearInterval(this.timer);\n\n this.leader?.destroy();\n\n if (this.channel) {\n this.channel.close();\n this.channel = undefined;\n }\n\n if (this.onStorageQueue && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorageQueue);\n }\n\n for (const off of this.exitHandlers) {\n off();\n }\n }\n\n private buildBatch(): TrackEvent[] {\n const batch: TrackEvent[] = [];\n const sentAt = new Date().toISOString();\n let index = 0;\n\n while (index < this.queue.length && batch.length < this.maxBatch) {\n const event = this.queue[index];\n const candidate = [...batch, event];\n const bytes = estimateEnvelopeBytes(candidate, this.sdkInfo, sentAt);\n\n if (bytes > this.maxPayloadBytes) {\n if (batch.length === 0) {\n const poisonKey = event.idempotencyKey;\n this.dropPoisonEvent(event);\n if (this.queue[index]?.idempotencyKey === poisonKey) {\n index++;\n }\n continue;\n }\n break;\n }\n\n batch.push(event);\n index++;\n }\n\n return batch;\n }\n\n private dropPoisonEvent(event: TrackEvent) {\n this.removeDelivered([event]);\n this.options.onEventsDropped?.(1);\n this.options.onDeliveryFailed?.({ status: 413, events: [event] });\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n }\n\n private removeDelivered(batch: TrackEvent[]) {\n const keys = new Set(batch.map((e) => e.idempotencyKey));\n this.queue = this.queue.filter((e) => !keys.has(e.idempotencyKey));\n }\n\n private async send(events: TrackEvent[]) {\n let payload: string;\n try {\n payload = JSON.stringify({\n sentAt: new Date().toISOString(),\n sdk: this.sdkInfo,\n events,\n });\n } catch {\n this.options.onDeliveryFailed?.({ status: 0, events });\n this.removeDelivered(events);\n return;\n }\n\n const payloadBytes = utf8ByteLength(payload);\n if (payloadBytes > this.maxPayloadBytes) {\n this.dropPoisonEvents(events);\n return;\n }\n\n const useKeepalive =\n this.runtime === \"browser\" &&\n typeof document !== \"undefined\" &&\n document.visibilityState === \"hidden\" &&\n payloadBytes <= this.maxPayloadBytes;\n\n const maxAttempts = Math.max(1, this.retries);\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const res = await this.fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: payload,\n signal: controller.signal,\n keepalive: useKeepalive,\n });\n\n if (res.status === 429 || res.status >= 500) {\n throw new RetryableDeliveryError();\n }\n\n if (res.status >= 400 && res.status < 500) {\n this.options.onDeliveryFailed?.({ status: res.status, events });\n this.removeDelivered(events);\n return;\n }\n\n if (!res.ok) {\n throw new RetryableDeliveryError();\n }\n\n return;\n } catch (err) {\n const isLast = attempt >= maxAttempts - 1;\n\n if (isLast) {\n throw err instanceof RetryableDeliveryError\n ? err\n : new RetryableDeliveryError();\n }\n\n await sleep(backoffMs(attempt + 1, this.retryDelay));\n } finally {\n clearTimeout(timeout);\n }\n }\n }\n\n private dropPoisonEvents(events: TrackEvent[]) {\n for (const event of events) {\n this.dropPoisonEvent(event);\n }\n }\n\n private loadAndMergeQueue() {\n const remote = this.storage.get(QUEUE_KEY);\n if (Array.isArray(remote)) {\n this.queue = mergeQueues(this.queue, remote as TrackEvent[]);\n }\n this.trimQueue();\n }\n\n private syncQueue() {\n const remote = this.storage.get(QUEUE_KEY);\n const merged = mergeQueues(\n Array.isArray(remote) ? (remote as TrackEvent[]) : [],\n this.queue,\n );\n this.queue = this.trimQueue(merged);\n this.storage.set(QUEUE_KEY, this.queue);\n this.broadcastQueue();\n }\n\n private trimQueue(queue: TrackEvent[] = this.queue): TrackEvent[] {\n if (queue.length <= this.maxQueue) return queue;\n const dropped = queue.length - this.maxQueue;\n this.options.onEventsDropped?.(dropped);\n return queue.slice(-this.maxQueue);\n }\n\n private broadcastQueue() {\n try {\n this.channel?.postMessage({\n type: \"queue-sync\",\n events: this.queue,\n });\n } catch {\n /* ignore */\n }\n }\n\n private setupQueueSync() {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(CHANNEL_NAME);\n this.channel.onmessage = (e: MessageEvent) => {\n if (e.data?.type !== \"queue-sync\" || !Array.isArray(e.data.events)) return;\n this.queue = mergeQueues(this.queue, e.data.events as TrackEvent[]);\n this.queue = this.trimQueue();\n };\n }\n\n if (typeof window !== \"undefined\") {\n this.onStorageQueue = (e: StorageEvent) => {\n if (e.key !== QUEUE_KEY) return;\n this.loadAndMergeQueue();\n };\n window.addEventListener(\"storage\", this.onStorageQueue);\n }\n }\n\n private startTimer() {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushInterval);\n }\n\n private setupBrowserExit() {\n const handler = () => {\n if (document.visibilityState === \"hidden\") {\n void this.flush();\n }\n };\n\n const pageHide = () => {\n void this.flush();\n };\n\n window.addEventListener(\"visibilitychange\", handler);\n window.addEventListener(\"pagehide\", pageHide);\n\n this.exitHandlers.push(() => {\n window.removeEventListener(\"visibilitychange\", handler);\n window.removeEventListener(\"pagehide\", pageHide);\n });\n }\n\n private setupProcessExit() {\n const proc = (\n globalThis as {\n process?: {\n on: (event: string, listener: (...args: unknown[]) => void) => void;\n once: (event: string, listener: (...args: unknown[]) => void) => void;\n off?: (event: string, listener: (...args: unknown[]) => void) => void;\n };\n }\n ).process;\n if (!proc?.on) return;\n\n const onSignal = () => {\n void this.shutdown();\n };\n\n proc.once(\"SIGINT\", onSignal);\n proc.once(\"SIGTERM\", onSignal);\n\n this.exitHandlers.push(() => {\n proc.off?.(\"SIGINT\", onSignal);\n proc.off?.(\"SIGTERM\", onSignal);\n });\n }\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/client.ts"],"sourcesContent":["export * from \"./client\";\nexport * from \"./types\";\n","import { TrackerOptions, TrackEvent, SdkInfo, TrackOptions } from \"./types\";\n\ndeclare const __SDK_VERSION__: string | undefined;\ndeclare const __EVENTRA_ENDPOINT__: string | undefined;\n\nconst SDK_NAME = \"@eventra_dev/eventra-sdk\";\nconst DEFAULT_ENDPOINT =\n (typeof __EVENTRA_ENDPOINT__ !== \"undefined\" && __EVENTRA_ENDPOINT__) ||\n \"https://api.eventra.dev/api/v1/ingest/batch\";\nconst SDK_VERSION =\n (typeof __SDK_VERSION__ !== \"undefined\" && __SDK_VERSION__) || \"0.0.0-dev\";\n\nconst LEADER_KEY = \"__eventra_leader__\";\nconst LEADER_TTL_MS = 4000;\nconst QUEUE_KEY = \"__eventra_q__\";\nconst CHANNEL_NAME = \"eventra-sdk\";\n\nconst MAX_EVENT_NAME = 64;\nconst MAX_USER_ID = 120;\nconst MAX_PROPERTIES_JSON_BYTES = 32_000;\nconst MAX_PROPERTIES_DEPTH = 8;\nconst DEFAULT_MAX_PAYLOAD_BYTES = 60_000;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst FETCH_TIMEOUT_MS = 5000;\nconst CIRCUIT_FAILURE_THRESHOLD = 5;\nconst CIRCUIT_COOLDOWN_MS = 5000;\n\nlet activeInstances = 0;\n\n// ================= RUNTIME =================\ntype Runtime = \"browser\" | \"node\" | \"edge\" | \"serverless\" | \"unknown\";\n\nfunction detectRuntime(): Runtime {\n const g = globalThis as typeof globalThis & {\n EdgeRuntime?: unknown;\n process?: { env?: Record<string, string>; versions?: { node?: string } };\n };\n\n if (typeof window !== \"undefined\" && !g.EdgeRuntime) return \"browser\";\n if (g.EdgeRuntime) return \"edge\";\n if (g.process?.env?.AWS_LAMBDA_FUNCTION_NAME) return \"serverless\";\n if (g.process?.versions?.node) return \"node\";\n\n return \"unknown\";\n}\n\n// ================= HELPERS =================\nfunction uuidV4(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n\n throw new Error(\"Eventra: crypto API unavailable — cannot generate idempotencyKey\");\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction truncate(value: string | undefined, max: number): string | undefined {\n if (value === undefined) return undefined;\n return value.length > max ? value.slice(0, max) : value;\n}\n\n/**\n * Never throws — `track()` is called fire-and-forget from arbitrary call sites,\n * so a malformed name must degrade gracefully instead of crashing the caller.\n * Returns `null` for an empty name (event should be dropped); otherwise trims\n * and truncates to `MAX_EVENT_NAME`, matching the documented behavior.\n */\nfunction normalizeEventName(name: string): string | null {\n const trimmed = (name ?? \"\").trim();\n if (!trimmed) return null;\n return trimmed.length > MAX_EVENT_NAME ? trimmed.slice(0, MAX_EVENT_NAME) : trimmed;\n}\n\nfunction backoffMs(attempt: number, base: number): number {\n const exp = Math.min(DEFAULT_MAX_RETRY_DELAY_MS, base * 2 ** attempt);\n const jitter = exp * (0.5 + Math.random() * 0.5);\n return jitter;\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== \"undefined\") {\n return new TextEncoder().encode(value).length;\n }\n const g = globalThis as { Buffer?: { byteLength: (s: string, enc: string) => number } };\n if (g.Buffer) {\n return g.Buffer.byteLength(value, \"utf8\");\n }\n return value.length;\n}\n\nfunction mergeQueues(...queues: TrackEvent[][]): TrackEvent[] {\n const seen = new Set<string>();\n const out: TrackEvent[] = [];\n\n for (const q of queues) {\n for (const e of q) {\n if (seen.has(e.idempotencyKey)) continue;\n seen.add(e.idempotencyKey);\n out.push(e);\n }\n }\n\n return out;\n}\n\nfunction validateProperties(\n properties: Record<string, unknown>,\n): Record<string, unknown> {\n const depth = (obj: unknown, level: number): void => {\n if (level > MAX_PROPERTIES_DEPTH) {\n throw new Error(`Eventra: properties exceed max depth (${MAX_PROPERTIES_DEPTH})`);\n }\n if (obj === null || typeof obj !== \"object\") return;\n if (Array.isArray(obj)) {\n for (const item of obj) depth(item, level + 1);\n return;\n }\n for (const value of Object.values(obj as Record<string, unknown>)) {\n depth(value, level + 1);\n }\n };\n\n depth(properties, 0);\n\n const json = JSON.stringify(properties);\n if (utf8ByteLength(json) > MAX_PROPERTIES_JSON_BYTES) {\n throw new Error(\n `Eventra: properties exceed max size (${MAX_PROPERTIES_JSON_BYTES} bytes)`,\n );\n }\n\n return properties;\n}\n\nfunction estimateEnvelopeBytes(\n events: TrackEvent[],\n sdk: SdkInfo,\n sentAt: string,\n): number {\n return utf8ByteLength(\n JSON.stringify({\n sentAt,\n sdk,\n events,\n }),\n );\n}\n\nclass RetryableDeliveryError extends Error {\n constructor(message = \"retryable delivery error\") {\n super(message);\n this.name = \"RetryableDeliveryError\";\n }\n}\n\n// ================= STORAGE =================\nclass Storage {\n private enabled = false;\n\n constructor() {\n try {\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(\"__t\", \"1\");\n localStorage.removeItem(\"__t\");\n this.enabled = true;\n }\n } catch {\n /* private mode / blocked storage */\n }\n }\n\n get(key: string): unknown {\n if (!this.enabled) return null;\n try {\n return JSON.parse(localStorage.getItem(key) || \"null\");\n } catch {\n return null;\n }\n }\n\n set(key: string, value: unknown) {\n if (!this.enabled) return;\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n /* quota exceeded */\n }\n }\n\n remove(key: string) {\n if (!this.enabled) return;\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n }\n}\n\n// ================= LEADER =================\nclass Leader {\n private tabId = uuidV4();\n private isLeader = false;\n private heartbeat?: ReturnType<typeof setInterval>;\n private election?: ReturnType<typeof setInterval>;\n private onStorage?: (e: StorageEvent) => void;\n\n constructor(private onLeadershipChange?: (isLeader: boolean) => void) {\n if (typeof localStorage === \"undefined\") {\n this.isLeader = true;\n return;\n }\n\n this.tick();\n\n this.election = setInterval(() => this.tick(), LEADER_TTL_MS / 2);\n\n if (typeof window !== \"undefined\") {\n this.onStorage = (e: StorageEvent) => {\n if (e.key === LEADER_KEY) this.tick();\n };\n window.addEventListener(\"storage\", this.onStorage);\n }\n }\n\n private tick() {\n const was = this.isLeader;\n\n if (this.isLeader) {\n if (!this.verifyLease()) {\n this.isLeader = false;\n } else {\n this.renew();\n }\n } else {\n this.isLeader = this.tryAcquire();\n if (this.isLeader) {\n if (!this.heartbeat) {\n this.heartbeat = setInterval(() => this.renew(), LEADER_TTL_MS / 2);\n }\n }\n }\n\n if (!this.isLeader && this.heartbeat) {\n clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n }\n\n if (was !== this.isLeader) {\n this.onLeadershipChange?.(this.isLeader);\n }\n }\n\n private tryAcquire(): boolean {\n try {\n const now = Date.now();\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) {\n this.renew();\n return true;\n }\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n if (now - parsed.ts > LEADER_TTL_MS) {\n this.renew();\n return true;\n }\n if (parsed.tabId === this.tabId) {\n this.renew();\n return true;\n }\n return false;\n } catch {\n return true;\n }\n }\n\n private verifyLease(): boolean {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) return false;\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n return (\n parsed.tabId === this.tabId && Date.now() - parsed.ts <= LEADER_TTL_MS\n );\n } catch {\n return false;\n }\n }\n\n private renew() {\n try {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: Date.now(), tabId: this.tabId }),\n );\n } catch {\n /* ignore */\n }\n }\n\n canSend() {\n return this.isLeader;\n }\n\n destroy() {\n if (this.election) clearInterval(this.election);\n if (this.heartbeat) clearInterval(this.heartbeat);\n if (this.onStorage && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorage);\n }\n if (this.isLeader && typeof localStorage !== \"undefined\") {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as { tabId: string };\n if (parsed.tabId === this.tabId) {\n localStorage.removeItem(LEADER_KEY);\n }\n }\n } catch {\n /* ignore */\n }\n }\n }\n}\n\n// ================= SDK =================\nexport class Eventra {\n private apiKey: string;\n private endpoint: string;\n private runtime: Runtime;\n private fetch: typeof fetch;\n\n private queue: TrackEvent[] = [];\n private sending: TrackEvent[] | null = null;\n private inFlight = false;\n private destroyed = false;\n private shuttingDown = false;\n\n private maxBatch: number;\n private maxQueue: number;\n private maxPayloadBytes: number;\n private flushInterval: number;\n private retries: number;\n private retryDelay: number;\n private persistQueue: boolean;\n\n private timer?: ReturnType<typeof setInterval>;\n\n private storage = new Storage();\n private leader: Leader | null = null;\n private channel?: BroadcastChannel;\n private onStorageQueue?: (e: StorageEvent) => void;\n\n private failureCount = 0;\n private circuitOpenUntil = 0;\n private circuitHalfOpen = false;\n\n private sdkInfo: SdkInfo;\n private options: TrackerOptions;\n\n private exitHandlers: Array<() => void> = [];\n\n constructor(options: TrackerOptions) {\n if (!options.apiKey) throw new Error(\"Eventra: apiKey required\");\n\n activeInstances++;\n if (activeInstances > 1 && typeof console !== \"undefined\") {\n console.warn(\n \"Eventra: multiple SDK instances detected — duplicate timers and sends are possible\",\n );\n }\n\n this.options = options;\n this.apiKey = options.apiKey;\n this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;\n this.runtime = detectRuntime();\n\n this.fetch = options.fetchImpl ?? globalThis.fetch;\n if (!this.fetch) {\n throw new Error(\"Eventra: fetch not available — provide fetchImpl\");\n }\n\n this.maxBatch = options.maxBatchSize ?? 50;\n this.maxQueue = options.maxQueueSize ?? 10000;\n this.maxPayloadBytes = options.maxPayloadBytes ?? DEFAULT_MAX_PAYLOAD_BYTES;\n this.flushInterval = options.flushInterval ?? 2000;\n this.retries = options.maxRetries ?? 3;\n this.retryDelay = options.retryBaseDelayMs ?? 300;\n // Queued events may carry userId/properties — default on for durability\n // across reloads, but let privacy-sensitive integrators opt out of ever\n // writing that data to localStorage.\n this.persistQueue = options.persistQueue ?? true;\n\n this.sdkInfo = {\n name: SDK_NAME,\n version: SDK_VERSION,\n runtime: this.runtime,\n };\n\n if (this.runtime === \"browser\") {\n if (this.persistQueue) {\n this.loadAndMergeQueue();\n }\n this.setupQueueSync();\n }\n\n if (this.runtime === \"browser\" && options.multiTabMode === \"leader\") {\n this.leader = new Leader((isLeader) => {\n if (isLeader) void this.flush();\n });\n }\n\n if (!options.disableTimer) {\n this.startTimer();\n }\n\n if (this.runtime === \"browser\") {\n this.setupBrowserExit();\n }\n\n if (options.autoFlushOnExit !== false && this.runtime !== \"browser\") {\n this.setupProcessExit();\n }\n }\n\n track(name: string, options?: TrackOptions) {\n if (this.destroyed || this.shuttingDown) return;\n\n const eventName = normalizeEventName(name);\n if (eventName === null) {\n this.options.onEventsDropped?.(1);\n return;\n }\n\n let properties: Record<string, unknown>;\n try {\n properties = validateProperties(options?.properties ?? {});\n } catch {\n // Oversized/too-deep properties can't be safely truncated — drop the\n // event instead of throwing out of a fire-and-forget call site.\n this.options.onEventsDropped?.(1);\n return;\n }\n\n if (this.queue.length >= this.maxQueue) {\n this.options.onEventsDropped?.(1);\n return;\n }\n\n const event: TrackEvent = {\n idempotencyKey: uuidV4(),\n name: eventName,\n userId: truncate(options?.userId, MAX_USER_ID),\n properties,\n timestamp: new Date().toISOString(),\n };\n\n this.queue.push(event);\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n\n if (this.queue.length >= this.maxBatch) {\n void this.flush();\n }\n\n if (this.runtime === \"serverless\") {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.inFlight || this.destroyed) return;\n if (!this.queue.length && !this.sending?.length) return;\n\n if (this.leader && !this.leader.canSend()) return;\n\n const now = Date.now();\n if (now < this.circuitOpenUntil) return;\n\n if (this.circuitOpenUntil > 0 && now >= this.circuitOpenUntil) {\n this.circuitHalfOpen = true;\n this.circuitOpenUntil = 0;\n }\n\n this.inFlight = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.buildBatch();\n if (!batch.length) break;\n\n this.sending = batch;\n\n try {\n await this.send(batch);\n this.removeDelivered(batch);\n this.failureCount = 0;\n this.circuitHalfOpen = false;\n } catch (err) {\n if (err instanceof RetryableDeliveryError) {\n this.failureCount++;\n if (this.circuitHalfOpen || this.failureCount >= CIRCUIT_FAILURE_THRESHOLD) {\n this.circuitOpenUntil = Date.now() + CIRCUIT_COOLDOWN_MS;\n this.circuitHalfOpen = false;\n }\n break;\n }\n break;\n } finally {\n this.sending = null;\n }\n\n if (this.circuitOpenUntil > Date.now()) break;\n }\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n } finally {\n this.inFlight = false;\n }\n }\n\n /** Flush pending events, then tear down timers and listeners */\n async shutdown(): Promise<void> {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n try {\n await this.flush();\n } finally {\n this.destroy();\n }\n }\n\n destroy() {\n if (this.destroyed) return;\n this.destroyed = true;\n activeInstances = Math.max(0, activeInstances - 1);\n\n if (this.timer) clearInterval(this.timer);\n\n this.leader?.destroy();\n\n if (this.channel) {\n this.channel.close();\n this.channel = undefined;\n }\n\n if (this.onStorageQueue && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorageQueue);\n }\n\n for (const off of this.exitHandlers) {\n off();\n }\n }\n\n private buildBatch(): TrackEvent[] {\n const batch: TrackEvent[] = [];\n const sentAt = new Date().toISOString();\n let index = 0;\n\n while (index < this.queue.length && batch.length < this.maxBatch) {\n const event = this.queue[index];\n const candidate = [...batch, event];\n const bytes = estimateEnvelopeBytes(candidate, this.sdkInfo, sentAt);\n\n if (bytes > this.maxPayloadBytes) {\n if (batch.length === 0) {\n const poisonKey = event.idempotencyKey;\n this.dropPoisonEvent(event);\n if (this.queue[index]?.idempotencyKey === poisonKey) {\n index++;\n }\n continue;\n }\n break;\n }\n\n batch.push(event);\n index++;\n }\n\n return batch;\n }\n\n private dropPoisonEvent(event: TrackEvent) {\n this.removeDelivered([event]);\n this.options.onEventsDropped?.(1);\n this.options.onDeliveryFailed?.({ status: 413, events: [event] });\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n }\n\n private removeDelivered(batch: TrackEvent[]) {\n const keys = new Set(batch.map((e) => e.idempotencyKey));\n this.queue = this.queue.filter((e) => !keys.has(e.idempotencyKey));\n }\n\n private async send(events: TrackEvent[]) {\n let payload: string;\n try {\n payload = JSON.stringify({\n sentAt: new Date().toISOString(),\n sdk: this.sdkInfo,\n events,\n });\n } catch {\n this.options.onDeliveryFailed?.({ status: 0, events });\n this.removeDelivered(events);\n return;\n }\n\n const payloadBytes = utf8ByteLength(payload);\n if (payloadBytes > this.maxPayloadBytes) {\n this.dropPoisonEvents(events);\n return;\n }\n\n const useKeepalive =\n this.runtime === \"browser\" &&\n typeof document !== \"undefined\" &&\n document.visibilityState === \"hidden\" &&\n payloadBytes <= this.maxPayloadBytes;\n\n const maxAttempts = Math.max(1, this.retries);\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const res = await this.fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: payload,\n signal: controller.signal,\n keepalive: useKeepalive,\n });\n\n if (res.status === 429 || res.status >= 500) {\n throw new RetryableDeliveryError();\n }\n\n if (res.status >= 400 && res.status < 500) {\n this.options.onDeliveryFailed?.({ status: res.status, events });\n this.removeDelivered(events);\n return;\n }\n\n if (!res.ok) {\n throw new RetryableDeliveryError();\n }\n\n return;\n } catch (err) {\n const isLast = attempt >= maxAttempts - 1;\n\n if (isLast) {\n throw err instanceof RetryableDeliveryError\n ? err\n : new RetryableDeliveryError();\n }\n\n await sleep(backoffMs(attempt + 1, this.retryDelay));\n } finally {\n clearTimeout(timeout);\n }\n }\n }\n\n private dropPoisonEvents(events: TrackEvent[]) {\n for (const event of events) {\n this.dropPoisonEvent(event);\n }\n }\n\n private loadAndMergeQueue() {\n const remote = this.storage.get(QUEUE_KEY);\n if (Array.isArray(remote)) {\n this.queue = mergeQueues(this.queue, remote as TrackEvent[]);\n }\n this.trimQueue();\n }\n\n private syncQueue() {\n if (this.persistQueue) {\n const remote = this.storage.get(QUEUE_KEY);\n const merged = mergeQueues(\n Array.isArray(remote) ? (remote as TrackEvent[]) : [],\n this.queue,\n );\n this.queue = this.trimQueue(merged);\n this.storage.set(QUEUE_KEY, this.queue);\n } else {\n this.queue = this.trimQueue();\n }\n this.broadcastQueue();\n }\n\n private trimQueue(queue: TrackEvent[] = this.queue): TrackEvent[] {\n if (queue.length <= this.maxQueue) return queue;\n const dropped = queue.length - this.maxQueue;\n this.options.onEventsDropped?.(dropped);\n return queue.slice(-this.maxQueue);\n }\n\n private broadcastQueue() {\n try {\n this.channel?.postMessage({\n type: \"queue-sync\",\n events: this.queue,\n });\n } catch {\n /* ignore */\n }\n }\n\n private setupQueueSync() {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(CHANNEL_NAME);\n this.channel.onmessage = (e: MessageEvent) => {\n if (e.data?.type !== \"queue-sync\" || !Array.isArray(e.data.events)) return;\n this.queue = mergeQueues(this.queue, e.data.events as TrackEvent[]);\n this.queue = this.trimQueue();\n };\n }\n\n if (this.persistQueue && typeof window !== \"undefined\") {\n this.onStorageQueue = (e: StorageEvent) => {\n if (e.key !== QUEUE_KEY) return;\n this.loadAndMergeQueue();\n };\n window.addEventListener(\"storage\", this.onStorageQueue);\n }\n }\n\n private startTimer() {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushInterval);\n }\n\n private setupBrowserExit() {\n const handler = () => {\n if (document.visibilityState === \"hidden\") {\n void this.flush();\n }\n };\n\n const pageHide = () => {\n void this.flush();\n };\n\n window.addEventListener(\"visibilitychange\", handler);\n window.addEventListener(\"pagehide\", pageHide);\n\n this.exitHandlers.push(() => {\n window.removeEventListener(\"visibilitychange\", handler);\n window.removeEventListener(\"pagehide\", pageHide);\n });\n }\n\n private setupProcessExit() {\n const proc = (\n globalThis as {\n process?: {\n on: (event: string, listener: (...args: unknown[]) => void) => void;\n once: (event: string, listener: (...args: unknown[]) => void) => void;\n off?: (event: string, listener: (...args: unknown[]) => void) => void;\n };\n }\n ).process;\n if (!proc?.on) return;\n\n const onSignal = () => {\n void this.shutdown();\n };\n\n proc.once(\"SIGINT\", onSignal);\n proc.once(\"SIGTERM\", onSignal);\n\n this.exitHandlers.push(() => {\n proc.off?.(\"SIGINT\", onSignal);\n proc.off?.(\"SIGTERM\", onSignal);\n });\n }\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -17,6 +17,14 @@ interface TrackerOptions {
|
|
|
17
17
|
fetchImpl?: typeof fetch;
|
|
18
18
|
autoFlushOnExit?: boolean;
|
|
19
19
|
disableTimer?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Persist the pending queue to `localStorage` in the browser so it survives
|
|
22
|
+
* reloads/tab closes (default `true`). Queued events can include `userId`
|
|
23
|
+
* and custom `properties`, which end up unencrypted in `localStorage` while
|
|
24
|
+
* this is on. Set to `false` to keep the queue in memory only — events are
|
|
25
|
+
* lost if the page is closed before they're delivered.
|
|
26
|
+
*/
|
|
27
|
+
persistQueue?: boolean;
|
|
20
28
|
/** Called when events are dropped due to queue overflow */
|
|
21
29
|
onEventsDropped?: (count: number) => void;
|
|
22
30
|
/** Called when the server rejects events (4xx except 429) */
|
|
@@ -56,6 +64,7 @@ declare class Eventra {
|
|
|
56
64
|
private flushInterval;
|
|
57
65
|
private retries;
|
|
58
66
|
private retryDelay;
|
|
67
|
+
private persistQueue;
|
|
59
68
|
private timer?;
|
|
60
69
|
private storage;
|
|
61
70
|
private leader;
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,14 @@ interface TrackerOptions {
|
|
|
17
17
|
fetchImpl?: typeof fetch;
|
|
18
18
|
autoFlushOnExit?: boolean;
|
|
19
19
|
disableTimer?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Persist the pending queue to `localStorage` in the browser so it survives
|
|
22
|
+
* reloads/tab closes (default `true`). Queued events can include `userId`
|
|
23
|
+
* and custom `properties`, which end up unencrypted in `localStorage` while
|
|
24
|
+
* this is on. Set to `false` to keep the queue in memory only — events are
|
|
25
|
+
* lost if the page is closed before they're delivered.
|
|
26
|
+
*/
|
|
27
|
+
persistQueue?: boolean;
|
|
20
28
|
/** Called when events are dropped due to queue overflow */
|
|
21
29
|
onEventsDropped?: (count: number) => void;
|
|
22
30
|
/** Called when the server rejects events (4xx except 429) */
|
|
@@ -56,6 +64,7 @@ declare class Eventra {
|
|
|
56
64
|
private flushInterval;
|
|
57
65
|
private retries;
|
|
58
66
|
private retryDelay;
|
|
67
|
+
private persistQueue;
|
|
59
68
|
private timer?;
|
|
60
69
|
private storage;
|
|
61
70
|
private leader;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
2
|
var SDK_NAME = "@eventra_dev/eventra-sdk";
|
|
3
3
|
var DEFAULT_ENDPOINT = "https://api.eventra.dev/api/v1/ingest/batch";
|
|
4
|
-
var SDK_VERSION = "1.2.
|
|
4
|
+
var SDK_VERSION = "1.2.41";
|
|
5
5
|
var LEADER_KEY = "__eventra_leader__";
|
|
6
6
|
var LEADER_TTL_MS = 4e3;
|
|
7
7
|
var QUEUE_KEY = "__eventra_q__";
|
|
@@ -45,15 +45,10 @@ function truncate(value, max) {
|
|
|
45
45
|
if (value === void 0) return void 0;
|
|
46
46
|
return value.length > max ? value.slice(0, max) : value;
|
|
47
47
|
}
|
|
48
|
-
function
|
|
49
|
-
const trimmed = name.trim();
|
|
50
|
-
if (!trimmed)
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
if (trimmed.length > MAX_EVENT_NAME) {
|
|
54
|
-
throw new Error(`Eventra: event name exceeds max length (${MAX_EVENT_NAME})`);
|
|
55
|
-
}
|
|
56
|
-
return trimmed;
|
|
48
|
+
function normalizeEventName(name) {
|
|
49
|
+
const trimmed = (name ?? "").trim();
|
|
50
|
+
if (!trimmed) return null;
|
|
51
|
+
return trimmed.length > MAX_EVENT_NAME ? trimmed.slice(0, MAX_EVENT_NAME) : trimmed;
|
|
57
52
|
}
|
|
58
53
|
function backoffMs(attempt, base) {
|
|
59
54
|
const exp = Math.min(DEFAULT_MAX_RETRY_DELAY_MS, base * 2 ** attempt);
|
|
@@ -295,13 +290,16 @@ var Eventra = class {
|
|
|
295
290
|
this.flushInterval = options.flushInterval ?? 2e3;
|
|
296
291
|
this.retries = options.maxRetries ?? 3;
|
|
297
292
|
this.retryDelay = options.retryBaseDelayMs ?? 300;
|
|
293
|
+
this.persistQueue = options.persistQueue ?? true;
|
|
298
294
|
this.sdkInfo = {
|
|
299
295
|
name: SDK_NAME,
|
|
300
296
|
version: SDK_VERSION,
|
|
301
297
|
runtime: this.runtime
|
|
302
298
|
};
|
|
303
299
|
if (this.runtime === "browser") {
|
|
304
|
-
this.
|
|
300
|
+
if (this.persistQueue) {
|
|
301
|
+
this.loadAndMergeQueue();
|
|
302
|
+
}
|
|
305
303
|
this.setupQueueSync();
|
|
306
304
|
}
|
|
307
305
|
if (this.runtime === "browser" && options.multiTabMode === "leader") {
|
|
@@ -321,14 +319,17 @@ var Eventra = class {
|
|
|
321
319
|
}
|
|
322
320
|
track(name, options) {
|
|
323
321
|
if (this.destroyed || this.shuttingDown) return;
|
|
324
|
-
|
|
322
|
+
const eventName = normalizeEventName(name);
|
|
323
|
+
if (eventName === null) {
|
|
324
|
+
this.options.onEventsDropped?.(1);
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
325
327
|
let properties;
|
|
326
328
|
try {
|
|
327
|
-
eventName = validateEventName(name);
|
|
328
329
|
properties = validateProperties(options?.properties ?? {});
|
|
329
|
-
} catch
|
|
330
|
-
|
|
331
|
-
|
|
330
|
+
} catch {
|
|
331
|
+
this.options.onEventsDropped?.(1);
|
|
332
|
+
return;
|
|
332
333
|
}
|
|
333
334
|
if (this.queue.length >= this.maxQueue) {
|
|
334
335
|
this.options.onEventsDropped?.(1);
|
|
@@ -528,13 +529,17 @@ var Eventra = class {
|
|
|
528
529
|
this.trimQueue();
|
|
529
530
|
}
|
|
530
531
|
syncQueue() {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
532
|
+
if (this.persistQueue) {
|
|
533
|
+
const remote = this.storage.get(QUEUE_KEY);
|
|
534
|
+
const merged = mergeQueues(
|
|
535
|
+
Array.isArray(remote) ? remote : [],
|
|
536
|
+
this.queue
|
|
537
|
+
);
|
|
538
|
+
this.queue = this.trimQueue(merged);
|
|
539
|
+
this.storage.set(QUEUE_KEY, this.queue);
|
|
540
|
+
} else {
|
|
541
|
+
this.queue = this.trimQueue();
|
|
542
|
+
}
|
|
538
543
|
this.broadcastQueue();
|
|
539
544
|
}
|
|
540
545
|
trimQueue(queue = this.queue) {
|
|
@@ -561,7 +566,7 @@ var Eventra = class {
|
|
|
561
566
|
this.queue = this.trimQueue();
|
|
562
567
|
};
|
|
563
568
|
}
|
|
564
|
-
if (typeof window !== "undefined") {
|
|
569
|
+
if (this.persistQueue && typeof window !== "undefined") {
|
|
565
570
|
this.onStorageQueue = (e) => {
|
|
566
571
|
if (e.key !== QUEUE_KEY) return;
|
|
567
572
|
this.loadAndMergeQueue();
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { TrackerOptions, TrackEvent, SdkInfo, TrackOptions } from \"./types\";\n\ndeclare const __SDK_VERSION__: string | undefined;\ndeclare const __EVENTRA_ENDPOINT__: string | undefined;\n\nconst SDK_NAME = \"@eventra_dev/eventra-sdk\";\nconst DEFAULT_ENDPOINT =\n (typeof __EVENTRA_ENDPOINT__ !== \"undefined\" && __EVENTRA_ENDPOINT__) ||\n \"https://api.eventra.dev/api/v1/ingest/batch\";\nconst SDK_VERSION =\n (typeof __SDK_VERSION__ !== \"undefined\" && __SDK_VERSION__) || \"0.0.0-dev\";\n\nconst LEADER_KEY = \"__eventra_leader__\";\nconst LEADER_TTL_MS = 4000;\nconst QUEUE_KEY = \"__eventra_q__\";\nconst CHANNEL_NAME = \"eventra-sdk\";\n\nconst MAX_EVENT_NAME = 64;\nconst MAX_USER_ID = 120;\nconst MAX_PROPERTIES_JSON_BYTES = 32_000;\nconst MAX_PROPERTIES_DEPTH = 8;\nconst DEFAULT_MAX_PAYLOAD_BYTES = 60_000;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst FETCH_TIMEOUT_MS = 5000;\nconst CIRCUIT_FAILURE_THRESHOLD = 5;\nconst CIRCUIT_COOLDOWN_MS = 5000;\n\nlet activeInstances = 0;\n\n// ================= RUNTIME =================\ntype Runtime = \"browser\" | \"node\" | \"edge\" | \"serverless\" | \"unknown\";\n\nfunction detectRuntime(): Runtime {\n const g = globalThis as typeof globalThis & {\n EdgeRuntime?: unknown;\n process?: { env?: Record<string, string>; versions?: { node?: string } };\n };\n\n if (typeof window !== \"undefined\" && !g.EdgeRuntime) return \"browser\";\n if (g.EdgeRuntime) return \"edge\";\n if (g.process?.env?.AWS_LAMBDA_FUNCTION_NAME) return \"serverless\";\n if (g.process?.versions?.node) return \"node\";\n\n return \"unknown\";\n}\n\n// ================= HELPERS =================\nfunction uuidV4(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n\n throw new Error(\"Eventra: crypto API unavailable — cannot generate idempotencyKey\");\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction truncate(value: string | undefined, max: number): string | undefined {\n if (value === undefined) return undefined;\n return value.length > max ? value.slice(0, max) : value;\n}\n\nfunction validateEventName(name: string): string {\n const trimmed = name.trim();\n if (!trimmed) {\n throw new Error(\"Eventra: event name is required\");\n }\n if (trimmed.length > MAX_EVENT_NAME) {\n throw new Error(`Eventra: event name exceeds max length (${MAX_EVENT_NAME})`);\n }\n return trimmed;\n}\n\nfunction backoffMs(attempt: number, base: number): number {\n const exp = Math.min(DEFAULT_MAX_RETRY_DELAY_MS, base * 2 ** attempt);\n const jitter = exp * (0.5 + Math.random() * 0.5);\n return jitter;\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== \"undefined\") {\n return new TextEncoder().encode(value).length;\n }\n const g = globalThis as { Buffer?: { byteLength: (s: string, enc: string) => number } };\n if (g.Buffer) {\n return g.Buffer.byteLength(value, \"utf8\");\n }\n return value.length;\n}\n\nfunction mergeQueues(...queues: TrackEvent[][]): TrackEvent[] {\n const seen = new Set<string>();\n const out: TrackEvent[] = [];\n\n for (const q of queues) {\n for (const e of q) {\n if (seen.has(e.idempotencyKey)) continue;\n seen.add(e.idempotencyKey);\n out.push(e);\n }\n }\n\n return out;\n}\n\nfunction validateProperties(\n properties: Record<string, unknown>,\n): Record<string, unknown> {\n const depth = (obj: unknown, level: number): void => {\n if (level > MAX_PROPERTIES_DEPTH) {\n throw new Error(`Eventra: properties exceed max depth (${MAX_PROPERTIES_DEPTH})`);\n }\n if (obj === null || typeof obj !== \"object\") return;\n if (Array.isArray(obj)) {\n for (const item of obj) depth(item, level + 1);\n return;\n }\n for (const value of Object.values(obj as Record<string, unknown>)) {\n depth(value, level + 1);\n }\n };\n\n depth(properties, 0);\n\n const json = JSON.stringify(properties);\n if (utf8ByteLength(json) > MAX_PROPERTIES_JSON_BYTES) {\n throw new Error(\n `Eventra: properties exceed max size (${MAX_PROPERTIES_JSON_BYTES} bytes)`,\n );\n }\n\n return properties;\n}\n\nfunction estimateEnvelopeBytes(\n events: TrackEvent[],\n sdk: SdkInfo,\n sentAt: string,\n): number {\n return utf8ByteLength(\n JSON.stringify({\n sentAt,\n sdk,\n events,\n }),\n );\n}\n\nclass RetryableDeliveryError extends Error {\n constructor(message = \"retryable delivery error\") {\n super(message);\n this.name = \"RetryableDeliveryError\";\n }\n}\n\n// ================= STORAGE =================\nclass Storage {\n private enabled = false;\n\n constructor() {\n try {\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(\"__t\", \"1\");\n localStorage.removeItem(\"__t\");\n this.enabled = true;\n }\n } catch {\n /* private mode / blocked storage */\n }\n }\n\n get(key: string): unknown {\n if (!this.enabled) return null;\n try {\n return JSON.parse(localStorage.getItem(key) || \"null\");\n } catch {\n return null;\n }\n }\n\n set(key: string, value: unknown) {\n if (!this.enabled) return;\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n /* quota exceeded */\n }\n }\n\n remove(key: string) {\n if (!this.enabled) return;\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n }\n}\n\n// ================= LEADER =================\nclass Leader {\n private tabId = uuidV4();\n private isLeader = false;\n private heartbeat?: ReturnType<typeof setInterval>;\n private election?: ReturnType<typeof setInterval>;\n private onStorage?: (e: StorageEvent) => void;\n\n constructor(private onLeadershipChange?: (isLeader: boolean) => void) {\n if (typeof localStorage === \"undefined\") {\n this.isLeader = true;\n return;\n }\n\n this.tick();\n\n this.election = setInterval(() => this.tick(), LEADER_TTL_MS / 2);\n\n if (typeof window !== \"undefined\") {\n this.onStorage = (e: StorageEvent) => {\n if (e.key === LEADER_KEY) this.tick();\n };\n window.addEventListener(\"storage\", this.onStorage);\n }\n }\n\n private tick() {\n const was = this.isLeader;\n\n if (this.isLeader) {\n if (!this.verifyLease()) {\n this.isLeader = false;\n } else {\n this.renew();\n }\n } else {\n this.isLeader = this.tryAcquire();\n if (this.isLeader) {\n if (!this.heartbeat) {\n this.heartbeat = setInterval(() => this.renew(), LEADER_TTL_MS / 2);\n }\n }\n }\n\n if (!this.isLeader && this.heartbeat) {\n clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n }\n\n if (was !== this.isLeader) {\n this.onLeadershipChange?.(this.isLeader);\n }\n }\n\n private tryAcquire(): boolean {\n try {\n const now = Date.now();\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) {\n this.renew();\n return true;\n }\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n if (now - parsed.ts > LEADER_TTL_MS) {\n this.renew();\n return true;\n }\n if (parsed.tabId === this.tabId) {\n this.renew();\n return true;\n }\n return false;\n } catch {\n return true;\n }\n }\n\n private verifyLease(): boolean {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) return false;\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n return (\n parsed.tabId === this.tabId && Date.now() - parsed.ts <= LEADER_TTL_MS\n );\n } catch {\n return false;\n }\n }\n\n private renew() {\n try {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: Date.now(), tabId: this.tabId }),\n );\n } catch {\n /* ignore */\n }\n }\n\n canSend() {\n return this.isLeader;\n }\n\n destroy() {\n if (this.election) clearInterval(this.election);\n if (this.heartbeat) clearInterval(this.heartbeat);\n if (this.onStorage && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorage);\n }\n if (this.isLeader && typeof localStorage !== \"undefined\") {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as { tabId: string };\n if (parsed.tabId === this.tabId) {\n localStorage.removeItem(LEADER_KEY);\n }\n }\n } catch {\n /* ignore */\n }\n }\n }\n}\n\n// ================= SDK =================\nexport class Eventra {\n private apiKey: string;\n private endpoint: string;\n private runtime: Runtime;\n private fetch: typeof fetch;\n\n private queue: TrackEvent[] = [];\n private sending: TrackEvent[] | null = null;\n private inFlight = false;\n private destroyed = false;\n private shuttingDown = false;\n\n private maxBatch: number;\n private maxQueue: number;\n private maxPayloadBytes: number;\n private flushInterval: number;\n private retries: number;\n private retryDelay: number;\n\n private timer?: ReturnType<typeof setInterval>;\n\n private storage = new Storage();\n private leader: Leader | null = null;\n private channel?: BroadcastChannel;\n private onStorageQueue?: (e: StorageEvent) => void;\n\n private failureCount = 0;\n private circuitOpenUntil = 0;\n private circuitHalfOpen = false;\n\n private sdkInfo: SdkInfo;\n private options: TrackerOptions;\n\n private exitHandlers: Array<() => void> = [];\n\n constructor(options: TrackerOptions) {\n if (!options.apiKey) throw new Error(\"Eventra: apiKey required\");\n\n activeInstances++;\n if (activeInstances > 1 && typeof console !== \"undefined\") {\n console.warn(\n \"Eventra: multiple SDK instances detected — duplicate timers and sends are possible\",\n );\n }\n\n this.options = options;\n this.apiKey = options.apiKey;\n this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;\n this.runtime = detectRuntime();\n\n this.fetch = options.fetchImpl ?? globalThis.fetch;\n if (!this.fetch) {\n throw new Error(\"Eventra: fetch not available — provide fetchImpl\");\n }\n\n this.maxBatch = options.maxBatchSize ?? 50;\n this.maxQueue = options.maxQueueSize ?? 10000;\n this.maxPayloadBytes = options.maxPayloadBytes ?? DEFAULT_MAX_PAYLOAD_BYTES;\n this.flushInterval = options.flushInterval ?? 2000;\n this.retries = options.maxRetries ?? 3;\n this.retryDelay = options.retryBaseDelayMs ?? 300;\n\n this.sdkInfo = {\n name: SDK_NAME,\n version: SDK_VERSION,\n runtime: this.runtime,\n };\n\n if (this.runtime === \"browser\") {\n this.loadAndMergeQueue();\n this.setupQueueSync();\n }\n\n if (this.runtime === \"browser\" && options.multiTabMode === \"leader\") {\n this.leader = new Leader((isLeader) => {\n if (isLeader) void this.flush();\n });\n }\n\n if (!options.disableTimer) {\n this.startTimer();\n }\n\n if (this.runtime === \"browser\") {\n this.setupBrowserExit();\n }\n\n if (options.autoFlushOnExit !== false && this.runtime !== \"browser\") {\n this.setupProcessExit();\n }\n }\n\n track(name: string, options?: TrackOptions) {\n if (this.destroyed || this.shuttingDown) return;\n\n let eventName: string;\n let properties: Record<string, unknown>;\n try {\n eventName = validateEventName(name);\n properties = validateProperties(options?.properties ?? {});\n } catch (err) {\n const message = err instanceof Error ? err.message : \"invalid event\";\n throw new Error(message);\n }\n\n if (this.queue.length >= this.maxQueue) {\n this.options.onEventsDropped?.(1);\n return;\n }\n\n const event: TrackEvent = {\n idempotencyKey: uuidV4(),\n name: eventName,\n userId: truncate(options?.userId, MAX_USER_ID),\n properties,\n timestamp: new Date().toISOString(),\n };\n\n this.queue.push(event);\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n\n if (this.queue.length >= this.maxBatch) {\n void this.flush();\n }\n\n if (this.runtime === \"serverless\") {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.inFlight || this.destroyed) return;\n if (!this.queue.length && !this.sending?.length) return;\n\n if (this.leader && !this.leader.canSend()) return;\n\n const now = Date.now();\n if (now < this.circuitOpenUntil) return;\n\n if (this.circuitOpenUntil > 0 && now >= this.circuitOpenUntil) {\n this.circuitHalfOpen = true;\n this.circuitOpenUntil = 0;\n }\n\n this.inFlight = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.buildBatch();\n if (!batch.length) break;\n\n this.sending = batch;\n\n try {\n await this.send(batch);\n this.removeDelivered(batch);\n this.failureCount = 0;\n this.circuitHalfOpen = false;\n } catch (err) {\n if (err instanceof RetryableDeliveryError) {\n this.failureCount++;\n if (this.circuitHalfOpen || this.failureCount >= CIRCUIT_FAILURE_THRESHOLD) {\n this.circuitOpenUntil = Date.now() + CIRCUIT_COOLDOWN_MS;\n this.circuitHalfOpen = false;\n }\n break;\n }\n break;\n } finally {\n this.sending = null;\n }\n\n if (this.circuitOpenUntil > Date.now()) break;\n }\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n } finally {\n this.inFlight = false;\n }\n }\n\n /** Flush pending events, then tear down timers and listeners */\n async shutdown(): Promise<void> {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n try {\n await this.flush();\n } finally {\n this.destroy();\n }\n }\n\n destroy() {\n if (this.destroyed) return;\n this.destroyed = true;\n activeInstances = Math.max(0, activeInstances - 1);\n\n if (this.timer) clearInterval(this.timer);\n\n this.leader?.destroy();\n\n if (this.channel) {\n this.channel.close();\n this.channel = undefined;\n }\n\n if (this.onStorageQueue && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorageQueue);\n }\n\n for (const off of this.exitHandlers) {\n off();\n }\n }\n\n private buildBatch(): TrackEvent[] {\n const batch: TrackEvent[] = [];\n const sentAt = new Date().toISOString();\n let index = 0;\n\n while (index < this.queue.length && batch.length < this.maxBatch) {\n const event = this.queue[index];\n const candidate = [...batch, event];\n const bytes = estimateEnvelopeBytes(candidate, this.sdkInfo, sentAt);\n\n if (bytes > this.maxPayloadBytes) {\n if (batch.length === 0) {\n const poisonKey = event.idempotencyKey;\n this.dropPoisonEvent(event);\n if (this.queue[index]?.idempotencyKey === poisonKey) {\n index++;\n }\n continue;\n }\n break;\n }\n\n batch.push(event);\n index++;\n }\n\n return batch;\n }\n\n private dropPoisonEvent(event: TrackEvent) {\n this.removeDelivered([event]);\n this.options.onEventsDropped?.(1);\n this.options.onDeliveryFailed?.({ status: 413, events: [event] });\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n }\n\n private removeDelivered(batch: TrackEvent[]) {\n const keys = new Set(batch.map((e) => e.idempotencyKey));\n this.queue = this.queue.filter((e) => !keys.has(e.idempotencyKey));\n }\n\n private async send(events: TrackEvent[]) {\n let payload: string;\n try {\n payload = JSON.stringify({\n sentAt: new Date().toISOString(),\n sdk: this.sdkInfo,\n events,\n });\n } catch {\n this.options.onDeliveryFailed?.({ status: 0, events });\n this.removeDelivered(events);\n return;\n }\n\n const payloadBytes = utf8ByteLength(payload);\n if (payloadBytes > this.maxPayloadBytes) {\n this.dropPoisonEvents(events);\n return;\n }\n\n const useKeepalive =\n this.runtime === \"browser\" &&\n typeof document !== \"undefined\" &&\n document.visibilityState === \"hidden\" &&\n payloadBytes <= this.maxPayloadBytes;\n\n const maxAttempts = Math.max(1, this.retries);\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const res = await this.fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: payload,\n signal: controller.signal,\n keepalive: useKeepalive,\n });\n\n if (res.status === 429 || res.status >= 500) {\n throw new RetryableDeliveryError();\n }\n\n if (res.status >= 400 && res.status < 500) {\n this.options.onDeliveryFailed?.({ status: res.status, events });\n this.removeDelivered(events);\n return;\n }\n\n if (!res.ok) {\n throw new RetryableDeliveryError();\n }\n\n return;\n } catch (err) {\n const isLast = attempt >= maxAttempts - 1;\n\n if (isLast) {\n throw err instanceof RetryableDeliveryError\n ? err\n : new RetryableDeliveryError();\n }\n\n await sleep(backoffMs(attempt + 1, this.retryDelay));\n } finally {\n clearTimeout(timeout);\n }\n }\n }\n\n private dropPoisonEvents(events: TrackEvent[]) {\n for (const event of events) {\n this.dropPoisonEvent(event);\n }\n }\n\n private loadAndMergeQueue() {\n const remote = this.storage.get(QUEUE_KEY);\n if (Array.isArray(remote)) {\n this.queue = mergeQueues(this.queue, remote as TrackEvent[]);\n }\n this.trimQueue();\n }\n\n private syncQueue() {\n const remote = this.storage.get(QUEUE_KEY);\n const merged = mergeQueues(\n Array.isArray(remote) ? (remote as TrackEvent[]) : [],\n this.queue,\n );\n this.queue = this.trimQueue(merged);\n this.storage.set(QUEUE_KEY, this.queue);\n this.broadcastQueue();\n }\n\n private trimQueue(queue: TrackEvent[] = this.queue): TrackEvent[] {\n if (queue.length <= this.maxQueue) return queue;\n const dropped = queue.length - this.maxQueue;\n this.options.onEventsDropped?.(dropped);\n return queue.slice(-this.maxQueue);\n }\n\n private broadcastQueue() {\n try {\n this.channel?.postMessage({\n type: \"queue-sync\",\n events: this.queue,\n });\n } catch {\n /* ignore */\n }\n }\n\n private setupQueueSync() {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(CHANNEL_NAME);\n this.channel.onmessage = (e: MessageEvent) => {\n if (e.data?.type !== \"queue-sync\" || !Array.isArray(e.data.events)) return;\n this.queue = mergeQueues(this.queue, e.data.events as TrackEvent[]);\n this.queue = this.trimQueue();\n };\n }\n\n if (typeof window !== \"undefined\") {\n this.onStorageQueue = (e: StorageEvent) => {\n if (e.key !== QUEUE_KEY) return;\n this.loadAndMergeQueue();\n };\n window.addEventListener(\"storage\", this.onStorageQueue);\n }\n }\n\n private startTimer() {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushInterval);\n }\n\n private setupBrowserExit() {\n const handler = () => {\n if (document.visibilityState === \"hidden\") {\n void this.flush();\n }\n };\n\n const pageHide = () => {\n void this.flush();\n };\n\n window.addEventListener(\"visibilitychange\", handler);\n window.addEventListener(\"pagehide\", pageHide);\n\n this.exitHandlers.push(() => {\n window.removeEventListener(\"visibilitychange\", handler);\n window.removeEventListener(\"pagehide\", pageHide);\n });\n }\n\n private setupProcessExit() {\n const proc = (\n globalThis as {\n process?: {\n on: (event: string, listener: (...args: unknown[]) => void) => void;\n once: (event: string, listener: (...args: unknown[]) => void) => void;\n off?: (event: string, listener: (...args: unknown[]) => void) => void;\n };\n }\n ).process;\n if (!proc?.on) return;\n\n const onSignal = () => {\n void this.shutdown();\n };\n\n proc.once(\"SIGINT\", onSignal);\n proc.once(\"SIGTERM\", onSignal);\n\n this.exitHandlers.push(() => {\n proc.off?.(\"SIGINT\", onSignal);\n proc.off?.(\"SIGTERM\", onSignal);\n });\n }\n}\n"],"mappings":";AAKA,IAAM,WAAW;AACjB,IAAM,mBAC4C;AAElD,IAAM,cACuC;AAE7C,IAAM,aAAa;AACnB,IAAM,gBAAgB;AACtB,IAAM,YAAY;AAClB,IAAM,eAAe;AAErB,IAAM,iBAAiB;AACvB,IAAM,cAAc;AACpB,IAAM,4BAA4B;AAClC,IAAM,uBAAuB;AAC7B,IAAM,4BAA4B;AAClC,IAAM,6BAA6B;AACnC,IAAM,mBAAmB;AACzB,IAAM,4BAA4B;AAClC,IAAM,sBAAsB;AAE5B,IAAI,kBAAkB;AAKtB,SAAS,gBAAyB;AAChC,QAAM,IAAI;AAKV,MAAI,OAAO,WAAW,eAAe,CAAC,EAAE,YAAa,QAAO;AAC5D,MAAI,EAAE,YAAa,QAAO;AAC1B,MAAI,EAAE,SAAS,KAAK,yBAA0B,QAAO;AACrD,MAAI,EAAE,SAAS,UAAU,KAAM,QAAO;AAEtC,SAAO;AACT;AAGA,SAAS,SAAiB;AACxB,MAAI,OAAO,WAAW,eAAe,OAAO,OAAO,eAAe,YAAY;AAC5E,WAAO,OAAO,WAAW;AAAA,EAC3B;AAEA,MAAI,OAAO,WAAW,eAAe,OAAO,iBAAiB;AAC3D,UAAM,QAAQ,IAAI,WAAW,EAAE;AAC/B,WAAO,gBAAgB,KAAK;AAC5B,UAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,UAAM,CAAC,IAAK,MAAM,CAAC,IAAI,KAAQ;AAC/B,UAAM,MAAM,CAAC,GAAG,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC1E,WAAO,GAAG,IAAI,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,MAAM,GAAG,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,IAAI,EAAE,CAAC,IAAI,IAAI,MAAM,EAAE,CAAC;AAAA,EAC1G;AAEA,QAAM,IAAI,MAAM,uEAAkE;AACpF;AAEA,SAAS,MAAM,IAAY;AACzB,SAAO,IAAI,QAAQ,CAAC,MAAM,WAAW,GAAG,EAAE,CAAC;AAC7C;AAEA,SAAS,SAAS,OAA2B,KAAiC;AAC5E,MAAI,UAAU,OAAW,QAAO;AAChC,SAAO,MAAM,SAAS,MAAM,MAAM,MAAM,GAAG,GAAG,IAAI;AACpD;AAEA,SAAS,kBAAkB,MAAsB;AAC/C,QAAM,UAAU,KAAK,KAAK;AAC1B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AACA,MAAI,QAAQ,SAAS,gBAAgB;AACnC,UAAM,IAAI,MAAM,2CAA2C,cAAc,GAAG;AAAA,EAC9E;AACA,SAAO;AACT;AAEA,SAAS,UAAU,SAAiB,MAAsB;AACxD,QAAM,MAAM,KAAK,IAAI,4BAA4B,OAAO,KAAK,OAAO;AACpE,QAAM,SAAS,OAAO,MAAM,KAAK,OAAO,IAAI;AAC5C,SAAO;AACT;AAEA,SAAS,eAAe,OAAuB;AAC7C,MAAI,OAAO,gBAAgB,aAAa;AACtC,WAAO,IAAI,YAAY,EAAE,OAAO,KAAK,EAAE;AAAA,EACzC;AACA,QAAM,IAAI;AACV,MAAI,EAAE,QAAQ;AACZ,WAAO,EAAE,OAAO,WAAW,OAAO,MAAM;AAAA,EAC1C;AACA,SAAO,MAAM;AACf;AAEA,SAAS,eAAe,QAAsC;AAC5D,QAAM,OAAO,oBAAI,IAAY;AAC7B,QAAM,MAAoB,CAAC;AAE3B,aAAW,KAAK,QAAQ;AACtB,eAAW,KAAK,GAAG;AACjB,UAAI,KAAK,IAAI,EAAE,cAAc,EAAG;AAChC,WAAK,IAAI,EAAE,cAAc;AACzB,UAAI,KAAK,CAAC;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,mBACP,YACyB;AACzB,QAAM,QAAQ,CAAC,KAAc,UAAwB;AACnD,QAAI,QAAQ,sBAAsB;AAChC,YAAM,IAAI,MAAM,yCAAyC,oBAAoB,GAAG;AAAA,IAClF;AACA,QAAI,QAAQ,QAAQ,OAAO,QAAQ,SAAU;AAC7C,QAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,iBAAW,QAAQ,IAAK,OAAM,MAAM,QAAQ,CAAC;AAC7C;AAAA,IACF;AACA,eAAW,SAAS,OAAO,OAAO,GAA8B,GAAG;AACjE,YAAM,OAAO,QAAQ,CAAC;AAAA,IACxB;AAAA,EACF;AAEA,QAAM,YAAY,CAAC;AAEnB,QAAM,OAAO,KAAK,UAAU,UAAU;AACtC,MAAI,eAAe,IAAI,IAAI,2BAA2B;AACpD,UAAM,IAAI;AAAA,MACR,wCAAwC,yBAAyB;AAAA,IACnE;AAAA,EACF;AAEA,SAAO;AACT;AAEA,SAAS,sBACP,QACA,KACA,QACQ;AACR,SAAO;AAAA,IACL,KAAK,UAAU;AAAA,MACb;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAEA,IAAM,yBAAN,cAAqC,MAAM;AAAA,EACzC,YAAY,UAAU,4BAA4B;AAChD,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAGA,IAAM,UAAN,MAAc;AAAA,EAGZ,cAAc;AAFd,SAAQ,UAAU;AAGhB,QAAI;AACF,UAAI,OAAO,iBAAiB,aAAa;AACvC,qBAAa,QAAQ,OAAO,GAAG;AAC/B,qBAAa,WAAW,KAAK;AAC7B,aAAK,UAAU;AAAA,MACjB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,IAAI,KAAsB;AACxB,QAAI,CAAC,KAAK,QAAS,QAAO;AAC1B,QAAI;AACF,aAAO,KAAK,MAAM,aAAa,QAAQ,GAAG,KAAK,MAAM;AAAA,IACvD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,IAAI,KAAa,OAAgB;AAC/B,QAAI,CAAC,KAAK,QAAS;AACnB,QAAI;AACF,mBAAa,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC;AAAA,IACjD,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,OAAO,KAAa;AAClB,QAAI,CAAC,KAAK,QAAS;AACnB,QAAI;AACF,mBAAa,WAAW,GAAG;AAAA,IAC7B,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAGA,IAAM,SAAN,MAAa;AAAA,EAOX,YAAoB,oBAAkD;AAAlD;AANpB,SAAQ,QAAQ,OAAO;AACvB,SAAQ,WAAW;AAMjB,QAAI,OAAO,iBAAiB,aAAa;AACvC,WAAK,WAAW;AAChB;AAAA,IACF;AAEA,SAAK,KAAK;AAEV,SAAK,WAAW,YAAY,MAAM,KAAK,KAAK,GAAG,gBAAgB,CAAC;AAEhE,QAAI,OAAO,WAAW,aAAa;AACjC,WAAK,YAAY,CAAC,MAAoB;AACpC,YAAI,EAAE,QAAQ,WAAY,MAAK,KAAK;AAAA,MACtC;AACA,aAAO,iBAAiB,WAAW,KAAK,SAAS;AAAA,IACnD;AAAA,EACF;AAAA,EAEQ,OAAO;AACb,UAAM,MAAM,KAAK;AAEjB,QAAI,KAAK,UAAU;AACjB,UAAI,CAAC,KAAK,YAAY,GAAG;AACvB,aAAK,WAAW;AAAA,MAClB,OAAO;AACL,aAAK,MAAM;AAAA,MACb;AAAA,IACF,OAAO;AACL,WAAK,WAAW,KAAK,WAAW;AAChC,UAAI,KAAK,UAAU;AACjB,YAAI,CAAC,KAAK,WAAW;AACnB,eAAK,YAAY,YAAY,MAAM,KAAK,MAAM,GAAG,gBAAgB,CAAC;AAAA,QACpE;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,KAAK,YAAY,KAAK,WAAW;AACpC,oBAAc,KAAK,SAAS;AAC5B,WAAK,YAAY;AAAA,IACnB;AAEA,QAAI,QAAQ,KAAK,UAAU;AACzB,WAAK,qBAAqB,KAAK,QAAQ;AAAA,IACzC;AAAA,EACF;AAAA,EAEQ,aAAsB;AAC5B,QAAI;AACF,YAAM,MAAM,KAAK,IAAI;AACrB,YAAM,MAAM,aAAa,QAAQ,UAAU;AAC3C,UAAI,CAAC,KAAK;AACR,aAAK,MAAM;AACX,eAAO;AAAA,MACT;AACA,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,UAAI,MAAM,OAAO,KAAK,eAAe;AACnC,aAAK,MAAM;AACX,eAAO;AAAA,MACT;AACA,UAAI,OAAO,UAAU,KAAK,OAAO;AAC/B,aAAK,MAAM;AACX,eAAO;AAAA,MACT;AACA,aAAO;AAAA,IACT,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,cAAuB;AAC7B,QAAI;AACF,YAAM,MAAM,aAAa,QAAQ,UAAU;AAC3C,UAAI,CAAC,IAAK,QAAO;AACjB,YAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,aACE,OAAO,UAAU,KAAK,SAAS,KAAK,IAAI,IAAI,OAAO,MAAM;AAAA,IAE7D,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEQ,QAAQ;AACd,QAAI;AACF,mBAAa;AAAA,QACX;AAAA,QACA,KAAK,UAAU,EAAE,IAAI,KAAK,IAAI,GAAG,OAAO,KAAK,MAAM,CAAC;AAAA,MACtD;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEA,UAAU;AACR,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,SAAU,eAAc,KAAK,QAAQ;AAC9C,QAAI,KAAK,UAAW,eAAc,KAAK,SAAS;AAChD,QAAI,KAAK,aAAa,OAAO,WAAW,aAAa;AACnD,aAAO,oBAAoB,WAAW,KAAK,SAAS;AAAA,IACtD;AACA,QAAI,KAAK,YAAY,OAAO,iBAAiB,aAAa;AACxD,UAAI;AACF,cAAM,MAAM,aAAa,QAAQ,UAAU;AAC3C,YAAI,KAAK;AACP,gBAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,cAAI,OAAO,UAAU,KAAK,OAAO;AAC/B,yBAAa,WAAW,UAAU;AAAA,UACpC;AAAA,QACF;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAGO,IAAM,UAAN,MAAc;AAAA,EAmCnB,YAAY,SAAyB;AA7BrC,SAAQ,QAAsB,CAAC;AAC/B,SAAQ,UAA+B;AACvC,SAAQ,WAAW;AACnB,SAAQ,YAAY;AACpB,SAAQ,eAAe;AAWvB,SAAQ,UAAU,IAAI,QAAQ;AAC9B,SAAQ,SAAwB;AAIhC,SAAQ,eAAe;AACvB,SAAQ,mBAAmB;AAC3B,SAAQ,kBAAkB;AAK1B,SAAQ,eAAkC,CAAC;AAGzC,QAAI,CAAC,QAAQ,OAAQ,OAAM,IAAI,MAAM,0BAA0B;AAE/D;AACA,QAAI,kBAAkB,KAAK,OAAO,YAAY,aAAa;AACzD,cAAQ;AAAA,QACN;AAAA,MACF;AAAA,IACF;AAEA,SAAK,UAAU;AACf,SAAK,SAAS,QAAQ;AACtB,SAAK,WAAW,QAAQ,YAAY;AACpC,SAAK,UAAU,cAAc;AAE7B,SAAK,QAAQ,QAAQ,aAAa,WAAW;AAC7C,QAAI,CAAC,KAAK,OAAO;AACf,YAAM,IAAI,MAAM,uDAAkD;AAAA,IACpE;AAEA,SAAK,WAAW,QAAQ,gBAAgB;AACxC,SAAK,WAAW,QAAQ,gBAAgB;AACxC,SAAK,kBAAkB,QAAQ,mBAAmB;AAClD,SAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,SAAK,UAAU,QAAQ,cAAc;AACrC,SAAK,aAAa,QAAQ,oBAAoB;AAE9C,SAAK,UAAU;AAAA,MACb,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS,KAAK;AAAA,IAChB;AAEA,QAAI,KAAK,YAAY,WAAW;AAC9B,WAAK,kBAAkB;AACvB,WAAK,eAAe;AAAA,IACtB;AAEA,QAAI,KAAK,YAAY,aAAa,QAAQ,iBAAiB,UAAU;AACnE,WAAK,SAAS,IAAI,OAAO,CAAC,aAAa;AACrC,YAAI,SAAU,MAAK,KAAK,MAAM;AAAA,MAChC,CAAC;AAAA,IACH;AAEA,QAAI,CAAC,QAAQ,cAAc;AACzB,WAAK,WAAW;AAAA,IAClB;AAEA,QAAI,KAAK,YAAY,WAAW;AAC9B,WAAK,iBAAiB;AAAA,IACxB;AAEA,QAAI,QAAQ,oBAAoB,SAAS,KAAK,YAAY,WAAW;AACnE,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAM,MAAc,SAAwB;AAC1C,QAAI,KAAK,aAAa,KAAK,aAAc;AAEzC,QAAI;AACJ,QAAI;AACJ,QAAI;AACF,kBAAY,kBAAkB,IAAI;AAClC,mBAAa,mBAAmB,SAAS,cAAc,CAAC,CAAC;AAAA,IAC3D,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;AACrD,YAAM,IAAI,MAAM,OAAO;AAAA,IACzB;AAEA,QAAI,KAAK,MAAM,UAAU,KAAK,UAAU;AACtC,WAAK,QAAQ,kBAAkB,CAAC;AAChC;AAAA,IACF;AAEA,UAAM,QAAoB;AAAA,MACxB,gBAAgB,OAAO;AAAA,MACvB,MAAM;AAAA,MACN,QAAQ,SAAS,SAAS,QAAQ,WAAW;AAAA,MAC7C;AAAA,MACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IACpC;AAEA,SAAK,MAAM,KAAK,KAAK;AAErB,QAAI,KAAK,YAAY,WAAW;AAC9B,WAAK,UAAU;AAAA,IACjB;AAEA,QAAI,KAAK,MAAM,UAAU,KAAK,UAAU;AACtC,WAAK,KAAK,MAAM;AAAA,IAClB;AAEA,QAAI,KAAK,YAAY,cAAc;AACjC,WAAK,KAAK,MAAM;AAAA,IAClB;AAAA,EACF;AAAA,EAEA,MAAM,QAAuB;AAC3B,QAAI,KAAK,YAAY,KAAK,UAAW;AACrC,QAAI,CAAC,KAAK,MAAM,UAAU,CAAC,KAAK,SAAS,OAAQ;AAEjD,QAAI,KAAK,UAAU,CAAC,KAAK,OAAO,QAAQ,EAAG;AAE3C,UAAM,MAAM,KAAK,IAAI;AACrB,QAAI,MAAM,KAAK,iBAAkB;AAEjC,QAAI,KAAK,mBAAmB,KAAK,OAAO,KAAK,kBAAkB;AAC7D,WAAK,kBAAkB;AACvB,WAAK,mBAAmB;AAAA,IAC1B;AAEA,SAAK,WAAW;AAEhB,QAAI;AACF,aAAO,KAAK,MAAM,SAAS,GAAG;AAC5B,cAAM,QAAQ,KAAK,WAAW;AAC9B,YAAI,CAAC,MAAM,OAAQ;AAEnB,aAAK,UAAU;AAEf,YAAI;AACF,gBAAM,KAAK,KAAK,KAAK;AACrB,eAAK,gBAAgB,KAAK;AAC1B,eAAK,eAAe;AACpB,eAAK,kBAAkB;AAAA,QACzB,SAAS,KAAK;AACZ,cAAI,eAAe,wBAAwB;AACzC,iBAAK;AACL,gBAAI,KAAK,mBAAmB,KAAK,gBAAgB,2BAA2B;AAC1E,mBAAK,mBAAmB,KAAK,IAAI,IAAI;AACrC,mBAAK,kBAAkB;AAAA,YACzB;AACA;AAAA,UACF;AACA;AAAA,QACF,UAAE;AACA,eAAK,UAAU;AAAA,QACjB;AAEA,YAAI,KAAK,mBAAmB,KAAK,IAAI,EAAG;AAAA,MAC1C;AAEA,UAAI,KAAK,YAAY,WAAW;AAC9B,aAAK,UAAU;AAAA,MACjB;AAAA,IACF,UAAE;AACA,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,WAA0B;AAC9B,QAAI,KAAK,aAAc;AACvB,SAAK,eAAe;AACpB,QAAI;AACF,YAAM,KAAK,MAAM;AAAA,IACnB,UAAE;AACA,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAAA,EAEA,UAAU;AACR,QAAI,KAAK,UAAW;AACpB,SAAK,YAAY;AACjB,sBAAkB,KAAK,IAAI,GAAG,kBAAkB,CAAC;AAEjD,QAAI,KAAK,MAAO,eAAc,KAAK,KAAK;AAExC,SAAK,QAAQ,QAAQ;AAErB,QAAI,KAAK,SAAS;AAChB,WAAK,QAAQ,MAAM;AACnB,WAAK,UAAU;AAAA,IACjB;AAEA,QAAI,KAAK,kBAAkB,OAAO,WAAW,aAAa;AACxD,aAAO,oBAAoB,WAAW,KAAK,cAAc;AAAA,IAC3D;AAEA,eAAW,OAAO,KAAK,cAAc;AACnC,UAAI;AAAA,IACN;AAAA,EACF;AAAA,EAEQ,aAA2B;AACjC,UAAM,QAAsB,CAAC;AAC7B,UAAM,UAAS,oBAAI,KAAK,GAAE,YAAY;AACtC,QAAI,QAAQ;AAEZ,WAAO,QAAQ,KAAK,MAAM,UAAU,MAAM,SAAS,KAAK,UAAU;AAChE,YAAM,QAAQ,KAAK,MAAM,KAAK;AAC9B,YAAM,YAAY,CAAC,GAAG,OAAO,KAAK;AAClC,YAAM,QAAQ,sBAAsB,WAAW,KAAK,SAAS,MAAM;AAEnE,UAAI,QAAQ,KAAK,iBAAiB;AAChC,YAAI,MAAM,WAAW,GAAG;AACtB,gBAAM,YAAY,MAAM;AACxB,eAAK,gBAAgB,KAAK;AAC1B,cAAI,KAAK,MAAM,KAAK,GAAG,mBAAmB,WAAW;AACnD;AAAA,UACF;AACA;AAAA,QACF;AACA;AAAA,MACF;AAEA,YAAM,KAAK,KAAK;AAChB;AAAA,IACF;AAEA,WAAO;AAAA,EACT;AAAA,EAEQ,gBAAgB,OAAmB;AACzC,SAAK,gBAAgB,CAAC,KAAK,CAAC;AAC5B,SAAK,QAAQ,kBAAkB,CAAC;AAChC,SAAK,QAAQ,mBAAmB,EAAE,QAAQ,KAAK,QAAQ,CAAC,KAAK,EAAE,CAAC;AAEhE,QAAI,KAAK,YAAY,WAAW;AAC9B,WAAK,UAAU;AAAA,IACjB;AAAA,EACF;AAAA,EAEQ,gBAAgB,OAAqB;AAC3C,UAAM,OAAO,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC;AACvD,SAAK,QAAQ,KAAK,MAAM,OAAO,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,cAAc,CAAC;AAAA,EACnE;AAAA,EAEA,MAAc,KAAK,QAAsB;AACvC,QAAI;AACJ,QAAI;AACF,gBAAU,KAAK,UAAU;AAAA,QACvB,SAAQ,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC/B,KAAK,KAAK;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH,QAAQ;AACN,WAAK,QAAQ,mBAAmB,EAAE,QAAQ,GAAG,OAAO,CAAC;AACrD,WAAK,gBAAgB,MAAM;AAC3B;AAAA,IACF;AAEA,UAAM,eAAe,eAAe,OAAO;AAC3C,QAAI,eAAe,KAAK,iBAAiB;AACvC,WAAK,iBAAiB,MAAM;AAC5B;AAAA,IACF;AAEA,UAAM,eACJ,KAAK,YAAY,aACjB,OAAO,aAAa,eACpB,SAAS,oBAAoB,YAC7B,gBAAgB,KAAK;AAEvB,UAAM,cAAc,KAAK,IAAI,GAAG,KAAK,OAAO;AAE5C,aAAS,UAAU,GAAG,UAAU,aAAa,WAAW;AACtD,YAAM,aAAa,IAAI,gBAAgB;AACvC,YAAM,UAAU,WAAW,MAAM,WAAW,MAAM,GAAG,gBAAgB;AAErE,UAAI;AACF,cAAM,MAAM,MAAM,KAAK,MAAM,KAAK,UAAU;AAAA,UAC1C,QAAQ;AAAA,UACR,SAAS;AAAA,YACP,gBAAgB;AAAA,YAChB,aAAa,KAAK;AAAA,UACpB;AAAA,UACA,MAAM;AAAA,UACN,QAAQ,WAAW;AAAA,UACnB,WAAW;AAAA,QACb,CAAC;AAED,YAAI,IAAI,WAAW,OAAO,IAAI,UAAU,KAAK;AAC3C,gBAAM,IAAI,uBAAuB;AAAA,QACnC;AAEA,YAAI,IAAI,UAAU,OAAO,IAAI,SAAS,KAAK;AACzC,eAAK,QAAQ,mBAAmB,EAAE,QAAQ,IAAI,QAAQ,OAAO,CAAC;AAC9D,eAAK,gBAAgB,MAAM;AAC3B;AAAA,QACF;AAEA,YAAI,CAAC,IAAI,IAAI;AACX,gBAAM,IAAI,uBAAuB;AAAA,QACnC;AAEA;AAAA,MACF,SAAS,KAAK;AACZ,cAAM,SAAS,WAAW,cAAc;AAExC,YAAI,QAAQ;AACV,gBAAM,eAAe,yBACjB,MACA,IAAI,uBAAuB;AAAA,QACjC;AAEA,cAAM,MAAM,UAAU,UAAU,GAAG,KAAK,UAAU,CAAC;AAAA,MACrD,UAAE;AACA,qBAAa,OAAO;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAAA,EAEQ,iBAAiB,QAAsB;AAC7C,eAAW,SAAS,QAAQ;AAC1B,WAAK,gBAAgB,KAAK;AAAA,IAC5B;AAAA,EACF;AAAA,EAEQ,oBAAoB;AAC1B,UAAM,SAAS,KAAK,QAAQ,IAAI,SAAS;AACzC,QAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,WAAK,QAAQ,YAAY,KAAK,OAAO,MAAsB;AAAA,IAC7D;AACA,SAAK,UAAU;AAAA,EACjB;AAAA,EAEQ,YAAY;AAClB,UAAM,SAAS,KAAK,QAAQ,IAAI,SAAS;AACzC,UAAM,SAAS;AAAA,MACb,MAAM,QAAQ,MAAM,IAAK,SAA0B,CAAC;AAAA,MACpD,KAAK;AAAA,IACP;AACA,SAAK,QAAQ,KAAK,UAAU,MAAM;AAClC,SAAK,QAAQ,IAAI,WAAW,KAAK,KAAK;AACtC,SAAK,eAAe;AAAA,EACtB;AAAA,EAEQ,UAAU,QAAsB,KAAK,OAAqB;AAChE,QAAI,MAAM,UAAU,KAAK,SAAU,QAAO;AAC1C,UAAM,UAAU,MAAM,SAAS,KAAK;AACpC,SAAK,QAAQ,kBAAkB,OAAO;AACtC,WAAO,MAAM,MAAM,CAAC,KAAK,QAAQ;AAAA,EACnC;AAAA,EAEQ,iBAAiB;AACvB,QAAI;AACF,WAAK,SAAS,YAAY;AAAA,QACxB,MAAM;AAAA,QACN,QAAQ,KAAK;AAAA,MACf,CAAC;AAAA,IACH,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,iBAAiB;AACvB,QAAI,OAAO,qBAAqB,aAAa;AAC3C,WAAK,UAAU,IAAI,iBAAiB,YAAY;AAChD,WAAK,QAAQ,YAAY,CAAC,MAAoB;AAC5C,YAAI,EAAE,MAAM,SAAS,gBAAgB,CAAC,MAAM,QAAQ,EAAE,KAAK,MAAM,EAAG;AACpE,aAAK,QAAQ,YAAY,KAAK,OAAO,EAAE,KAAK,MAAsB;AAClE,aAAK,QAAQ,KAAK,UAAU;AAAA,MAC9B;AAAA,IACF;AAEA,QAAI,OAAO,WAAW,aAAa;AACjC,WAAK,iBAAiB,CAAC,MAAoB;AACzC,YAAI,EAAE,QAAQ,UAAW;AACzB,aAAK,kBAAkB;AAAA,MACzB;AACA,aAAO,iBAAiB,WAAW,KAAK,cAAc;AAAA,IACxD;AAAA,EACF;AAAA,EAEQ,aAAa;AACnB,SAAK,QAAQ,YAAY,MAAM;AAC7B,WAAK,KAAK,MAAM;AAAA,IAClB,GAAG,KAAK,aAAa;AAAA,EACvB;AAAA,EAEQ,mBAAmB;AACzB,UAAM,UAAU,MAAM;AACpB,UAAI,SAAS,oBAAoB,UAAU;AACzC,aAAK,KAAK,MAAM;AAAA,MAClB;AAAA,IACF;AAEA,UAAM,WAAW,MAAM;AACrB,WAAK,KAAK,MAAM;AAAA,IAClB;AAEA,WAAO,iBAAiB,oBAAoB,OAAO;AACnD,WAAO,iBAAiB,YAAY,QAAQ;AAE5C,SAAK,aAAa,KAAK,MAAM;AAC3B,aAAO,oBAAoB,oBAAoB,OAAO;AACtD,aAAO,oBAAoB,YAAY,QAAQ;AAAA,IACjD,CAAC;AAAA,EACH;AAAA,EAEQ,mBAAmB;AACzB,UAAM,OACJ,WAOA;AACF,QAAI,CAAC,MAAM,GAAI;AAEf,UAAM,WAAW,MAAM;AACrB,WAAK,KAAK,SAAS;AAAA,IACrB;AAEA,SAAK,KAAK,UAAU,QAAQ;AAC5B,SAAK,KAAK,WAAW,QAAQ;AAE7B,SAAK,aAAa,KAAK,MAAM;AAC3B,WAAK,MAAM,UAAU,QAAQ;AAC7B,WAAK,MAAM,WAAW,QAAQ;AAAA,IAChC,CAAC;AAAA,EACH;AACF;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { TrackerOptions, TrackEvent, SdkInfo, TrackOptions } from \"./types\";\n\ndeclare const __SDK_VERSION__: string | undefined;\ndeclare const __EVENTRA_ENDPOINT__: string | undefined;\n\nconst SDK_NAME = \"@eventra_dev/eventra-sdk\";\nconst DEFAULT_ENDPOINT =\n (typeof __EVENTRA_ENDPOINT__ !== \"undefined\" && __EVENTRA_ENDPOINT__) ||\n \"https://api.eventra.dev/api/v1/ingest/batch\";\nconst SDK_VERSION =\n (typeof __SDK_VERSION__ !== \"undefined\" && __SDK_VERSION__) || \"0.0.0-dev\";\n\nconst LEADER_KEY = \"__eventra_leader__\";\nconst LEADER_TTL_MS = 4000;\nconst QUEUE_KEY = \"__eventra_q__\";\nconst CHANNEL_NAME = \"eventra-sdk\";\n\nconst MAX_EVENT_NAME = 64;\nconst MAX_USER_ID = 120;\nconst MAX_PROPERTIES_JSON_BYTES = 32_000;\nconst MAX_PROPERTIES_DEPTH = 8;\nconst DEFAULT_MAX_PAYLOAD_BYTES = 60_000;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 30_000;\nconst FETCH_TIMEOUT_MS = 5000;\nconst CIRCUIT_FAILURE_THRESHOLD = 5;\nconst CIRCUIT_COOLDOWN_MS = 5000;\n\nlet activeInstances = 0;\n\n// ================= RUNTIME =================\ntype Runtime = \"browser\" | \"node\" | \"edge\" | \"serverless\" | \"unknown\";\n\nfunction detectRuntime(): Runtime {\n const g = globalThis as typeof globalThis & {\n EdgeRuntime?: unknown;\n process?: { env?: Record<string, string>; versions?: { node?: string } };\n };\n\n if (typeof window !== \"undefined\" && !g.EdgeRuntime) return \"browser\";\n if (g.EdgeRuntime) return \"edge\";\n if (g.process?.env?.AWS_LAMBDA_FUNCTION_NAME) return \"serverless\";\n if (g.process?.versions?.node) return \"node\";\n\n return \"unknown\";\n}\n\n// ================= HELPERS =================\nfunction uuidV4(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n\n if (typeof crypto !== \"undefined\" && crypto.getRandomValues) {\n const bytes = new Uint8Array(16);\n crypto.getRandomValues(bytes);\n bytes[6] = (bytes[6] & 0x0f) | 0x40;\n bytes[8] = (bytes[8] & 0x3f) | 0x80;\n const hex = [...bytes].map((b) => b.toString(16).padStart(2, \"0\")).join(\"\");\n return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;\n }\n\n throw new Error(\"Eventra: crypto API unavailable — cannot generate idempotencyKey\");\n}\n\nfunction sleep(ms: number) {\n return new Promise((r) => setTimeout(r, ms));\n}\n\nfunction truncate(value: string | undefined, max: number): string | undefined {\n if (value === undefined) return undefined;\n return value.length > max ? value.slice(0, max) : value;\n}\n\n/**\n * Never throws — `track()` is called fire-and-forget from arbitrary call sites,\n * so a malformed name must degrade gracefully instead of crashing the caller.\n * Returns `null` for an empty name (event should be dropped); otherwise trims\n * and truncates to `MAX_EVENT_NAME`, matching the documented behavior.\n */\nfunction normalizeEventName(name: string): string | null {\n const trimmed = (name ?? \"\").trim();\n if (!trimmed) return null;\n return trimmed.length > MAX_EVENT_NAME ? trimmed.slice(0, MAX_EVENT_NAME) : trimmed;\n}\n\nfunction backoffMs(attempt: number, base: number): number {\n const exp = Math.min(DEFAULT_MAX_RETRY_DELAY_MS, base * 2 ** attempt);\n const jitter = exp * (0.5 + Math.random() * 0.5);\n return jitter;\n}\n\nfunction utf8ByteLength(value: string): number {\n if (typeof TextEncoder !== \"undefined\") {\n return new TextEncoder().encode(value).length;\n }\n const g = globalThis as { Buffer?: { byteLength: (s: string, enc: string) => number } };\n if (g.Buffer) {\n return g.Buffer.byteLength(value, \"utf8\");\n }\n return value.length;\n}\n\nfunction mergeQueues(...queues: TrackEvent[][]): TrackEvent[] {\n const seen = new Set<string>();\n const out: TrackEvent[] = [];\n\n for (const q of queues) {\n for (const e of q) {\n if (seen.has(e.idempotencyKey)) continue;\n seen.add(e.idempotencyKey);\n out.push(e);\n }\n }\n\n return out;\n}\n\nfunction validateProperties(\n properties: Record<string, unknown>,\n): Record<string, unknown> {\n const depth = (obj: unknown, level: number): void => {\n if (level > MAX_PROPERTIES_DEPTH) {\n throw new Error(`Eventra: properties exceed max depth (${MAX_PROPERTIES_DEPTH})`);\n }\n if (obj === null || typeof obj !== \"object\") return;\n if (Array.isArray(obj)) {\n for (const item of obj) depth(item, level + 1);\n return;\n }\n for (const value of Object.values(obj as Record<string, unknown>)) {\n depth(value, level + 1);\n }\n };\n\n depth(properties, 0);\n\n const json = JSON.stringify(properties);\n if (utf8ByteLength(json) > MAX_PROPERTIES_JSON_BYTES) {\n throw new Error(\n `Eventra: properties exceed max size (${MAX_PROPERTIES_JSON_BYTES} bytes)`,\n );\n }\n\n return properties;\n}\n\nfunction estimateEnvelopeBytes(\n events: TrackEvent[],\n sdk: SdkInfo,\n sentAt: string,\n): number {\n return utf8ByteLength(\n JSON.stringify({\n sentAt,\n sdk,\n events,\n }),\n );\n}\n\nclass RetryableDeliveryError extends Error {\n constructor(message = \"retryable delivery error\") {\n super(message);\n this.name = \"RetryableDeliveryError\";\n }\n}\n\n// ================= STORAGE =================\nclass Storage {\n private enabled = false;\n\n constructor() {\n try {\n if (typeof localStorage !== \"undefined\") {\n localStorage.setItem(\"__t\", \"1\");\n localStorage.removeItem(\"__t\");\n this.enabled = true;\n }\n } catch {\n /* private mode / blocked storage */\n }\n }\n\n get(key: string): unknown {\n if (!this.enabled) return null;\n try {\n return JSON.parse(localStorage.getItem(key) || \"null\");\n } catch {\n return null;\n }\n }\n\n set(key: string, value: unknown) {\n if (!this.enabled) return;\n try {\n localStorage.setItem(key, JSON.stringify(value));\n } catch {\n /* quota exceeded */\n }\n }\n\n remove(key: string) {\n if (!this.enabled) return;\n try {\n localStorage.removeItem(key);\n } catch {\n /* ignore */\n }\n }\n}\n\n// ================= LEADER =================\nclass Leader {\n private tabId = uuidV4();\n private isLeader = false;\n private heartbeat?: ReturnType<typeof setInterval>;\n private election?: ReturnType<typeof setInterval>;\n private onStorage?: (e: StorageEvent) => void;\n\n constructor(private onLeadershipChange?: (isLeader: boolean) => void) {\n if (typeof localStorage === \"undefined\") {\n this.isLeader = true;\n return;\n }\n\n this.tick();\n\n this.election = setInterval(() => this.tick(), LEADER_TTL_MS / 2);\n\n if (typeof window !== \"undefined\") {\n this.onStorage = (e: StorageEvent) => {\n if (e.key === LEADER_KEY) this.tick();\n };\n window.addEventListener(\"storage\", this.onStorage);\n }\n }\n\n private tick() {\n const was = this.isLeader;\n\n if (this.isLeader) {\n if (!this.verifyLease()) {\n this.isLeader = false;\n } else {\n this.renew();\n }\n } else {\n this.isLeader = this.tryAcquire();\n if (this.isLeader) {\n if (!this.heartbeat) {\n this.heartbeat = setInterval(() => this.renew(), LEADER_TTL_MS / 2);\n }\n }\n }\n\n if (!this.isLeader && this.heartbeat) {\n clearInterval(this.heartbeat);\n this.heartbeat = undefined;\n }\n\n if (was !== this.isLeader) {\n this.onLeadershipChange?.(this.isLeader);\n }\n }\n\n private tryAcquire(): boolean {\n try {\n const now = Date.now();\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) {\n this.renew();\n return true;\n }\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n if (now - parsed.ts > LEADER_TTL_MS) {\n this.renew();\n return true;\n }\n if (parsed.tabId === this.tabId) {\n this.renew();\n return true;\n }\n return false;\n } catch {\n return true;\n }\n }\n\n private verifyLease(): boolean {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (!raw) return false;\n const parsed = JSON.parse(raw) as { ts: number; tabId: string };\n return (\n parsed.tabId === this.tabId && Date.now() - parsed.ts <= LEADER_TTL_MS\n );\n } catch {\n return false;\n }\n }\n\n private renew() {\n try {\n localStorage.setItem(\n LEADER_KEY,\n JSON.stringify({ ts: Date.now(), tabId: this.tabId }),\n );\n } catch {\n /* ignore */\n }\n }\n\n canSend() {\n return this.isLeader;\n }\n\n destroy() {\n if (this.election) clearInterval(this.election);\n if (this.heartbeat) clearInterval(this.heartbeat);\n if (this.onStorage && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorage);\n }\n if (this.isLeader && typeof localStorage !== \"undefined\") {\n try {\n const raw = localStorage.getItem(LEADER_KEY);\n if (raw) {\n const parsed = JSON.parse(raw) as { tabId: string };\n if (parsed.tabId === this.tabId) {\n localStorage.removeItem(LEADER_KEY);\n }\n }\n } catch {\n /* ignore */\n }\n }\n }\n}\n\n// ================= SDK =================\nexport class Eventra {\n private apiKey: string;\n private endpoint: string;\n private runtime: Runtime;\n private fetch: typeof fetch;\n\n private queue: TrackEvent[] = [];\n private sending: TrackEvent[] | null = null;\n private inFlight = false;\n private destroyed = false;\n private shuttingDown = false;\n\n private maxBatch: number;\n private maxQueue: number;\n private maxPayloadBytes: number;\n private flushInterval: number;\n private retries: number;\n private retryDelay: number;\n private persistQueue: boolean;\n\n private timer?: ReturnType<typeof setInterval>;\n\n private storage = new Storage();\n private leader: Leader | null = null;\n private channel?: BroadcastChannel;\n private onStorageQueue?: (e: StorageEvent) => void;\n\n private failureCount = 0;\n private circuitOpenUntil = 0;\n private circuitHalfOpen = false;\n\n private sdkInfo: SdkInfo;\n private options: TrackerOptions;\n\n private exitHandlers: Array<() => void> = [];\n\n constructor(options: TrackerOptions) {\n if (!options.apiKey) throw new Error(\"Eventra: apiKey required\");\n\n activeInstances++;\n if (activeInstances > 1 && typeof console !== \"undefined\") {\n console.warn(\n \"Eventra: multiple SDK instances detected — duplicate timers and sends are possible\",\n );\n }\n\n this.options = options;\n this.apiKey = options.apiKey;\n this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;\n this.runtime = detectRuntime();\n\n this.fetch = options.fetchImpl ?? globalThis.fetch;\n if (!this.fetch) {\n throw new Error(\"Eventra: fetch not available — provide fetchImpl\");\n }\n\n this.maxBatch = options.maxBatchSize ?? 50;\n this.maxQueue = options.maxQueueSize ?? 10000;\n this.maxPayloadBytes = options.maxPayloadBytes ?? DEFAULT_MAX_PAYLOAD_BYTES;\n this.flushInterval = options.flushInterval ?? 2000;\n this.retries = options.maxRetries ?? 3;\n this.retryDelay = options.retryBaseDelayMs ?? 300;\n // Queued events may carry userId/properties — default on for durability\n // across reloads, but let privacy-sensitive integrators opt out of ever\n // writing that data to localStorage.\n this.persistQueue = options.persistQueue ?? true;\n\n this.sdkInfo = {\n name: SDK_NAME,\n version: SDK_VERSION,\n runtime: this.runtime,\n };\n\n if (this.runtime === \"browser\") {\n if (this.persistQueue) {\n this.loadAndMergeQueue();\n }\n this.setupQueueSync();\n }\n\n if (this.runtime === \"browser\" && options.multiTabMode === \"leader\") {\n this.leader = new Leader((isLeader) => {\n if (isLeader) void this.flush();\n });\n }\n\n if (!options.disableTimer) {\n this.startTimer();\n }\n\n if (this.runtime === \"browser\") {\n this.setupBrowserExit();\n }\n\n if (options.autoFlushOnExit !== false && this.runtime !== \"browser\") {\n this.setupProcessExit();\n }\n }\n\n track(name: string, options?: TrackOptions) {\n if (this.destroyed || this.shuttingDown) return;\n\n const eventName = normalizeEventName(name);\n if (eventName === null) {\n this.options.onEventsDropped?.(1);\n return;\n }\n\n let properties: Record<string, unknown>;\n try {\n properties = validateProperties(options?.properties ?? {});\n } catch {\n // Oversized/too-deep properties can't be safely truncated — drop the\n // event instead of throwing out of a fire-and-forget call site.\n this.options.onEventsDropped?.(1);\n return;\n }\n\n if (this.queue.length >= this.maxQueue) {\n this.options.onEventsDropped?.(1);\n return;\n }\n\n const event: TrackEvent = {\n idempotencyKey: uuidV4(),\n name: eventName,\n userId: truncate(options?.userId, MAX_USER_ID),\n properties,\n timestamp: new Date().toISOString(),\n };\n\n this.queue.push(event);\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n\n if (this.queue.length >= this.maxBatch) {\n void this.flush();\n }\n\n if (this.runtime === \"serverless\") {\n void this.flush();\n }\n }\n\n async flush(): Promise<void> {\n if (this.inFlight || this.destroyed) return;\n if (!this.queue.length && !this.sending?.length) return;\n\n if (this.leader && !this.leader.canSend()) return;\n\n const now = Date.now();\n if (now < this.circuitOpenUntil) return;\n\n if (this.circuitOpenUntil > 0 && now >= this.circuitOpenUntil) {\n this.circuitHalfOpen = true;\n this.circuitOpenUntil = 0;\n }\n\n this.inFlight = true;\n\n try {\n while (this.queue.length > 0) {\n const batch = this.buildBatch();\n if (!batch.length) break;\n\n this.sending = batch;\n\n try {\n await this.send(batch);\n this.removeDelivered(batch);\n this.failureCount = 0;\n this.circuitHalfOpen = false;\n } catch (err) {\n if (err instanceof RetryableDeliveryError) {\n this.failureCount++;\n if (this.circuitHalfOpen || this.failureCount >= CIRCUIT_FAILURE_THRESHOLD) {\n this.circuitOpenUntil = Date.now() + CIRCUIT_COOLDOWN_MS;\n this.circuitHalfOpen = false;\n }\n break;\n }\n break;\n } finally {\n this.sending = null;\n }\n\n if (this.circuitOpenUntil > Date.now()) break;\n }\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n } finally {\n this.inFlight = false;\n }\n }\n\n /** Flush pending events, then tear down timers and listeners */\n async shutdown(): Promise<void> {\n if (this.shuttingDown) return;\n this.shuttingDown = true;\n try {\n await this.flush();\n } finally {\n this.destroy();\n }\n }\n\n destroy() {\n if (this.destroyed) return;\n this.destroyed = true;\n activeInstances = Math.max(0, activeInstances - 1);\n\n if (this.timer) clearInterval(this.timer);\n\n this.leader?.destroy();\n\n if (this.channel) {\n this.channel.close();\n this.channel = undefined;\n }\n\n if (this.onStorageQueue && typeof window !== \"undefined\") {\n window.removeEventListener(\"storage\", this.onStorageQueue);\n }\n\n for (const off of this.exitHandlers) {\n off();\n }\n }\n\n private buildBatch(): TrackEvent[] {\n const batch: TrackEvent[] = [];\n const sentAt = new Date().toISOString();\n let index = 0;\n\n while (index < this.queue.length && batch.length < this.maxBatch) {\n const event = this.queue[index];\n const candidate = [...batch, event];\n const bytes = estimateEnvelopeBytes(candidate, this.sdkInfo, sentAt);\n\n if (bytes > this.maxPayloadBytes) {\n if (batch.length === 0) {\n const poisonKey = event.idempotencyKey;\n this.dropPoisonEvent(event);\n if (this.queue[index]?.idempotencyKey === poisonKey) {\n index++;\n }\n continue;\n }\n break;\n }\n\n batch.push(event);\n index++;\n }\n\n return batch;\n }\n\n private dropPoisonEvent(event: TrackEvent) {\n this.removeDelivered([event]);\n this.options.onEventsDropped?.(1);\n this.options.onDeliveryFailed?.({ status: 413, events: [event] });\n\n if (this.runtime === \"browser\") {\n this.syncQueue();\n }\n }\n\n private removeDelivered(batch: TrackEvent[]) {\n const keys = new Set(batch.map((e) => e.idempotencyKey));\n this.queue = this.queue.filter((e) => !keys.has(e.idempotencyKey));\n }\n\n private async send(events: TrackEvent[]) {\n let payload: string;\n try {\n payload = JSON.stringify({\n sentAt: new Date().toISOString(),\n sdk: this.sdkInfo,\n events,\n });\n } catch {\n this.options.onDeliveryFailed?.({ status: 0, events });\n this.removeDelivered(events);\n return;\n }\n\n const payloadBytes = utf8ByteLength(payload);\n if (payloadBytes > this.maxPayloadBytes) {\n this.dropPoisonEvents(events);\n return;\n }\n\n const useKeepalive =\n this.runtime === \"browser\" &&\n typeof document !== \"undefined\" &&\n document.visibilityState === \"hidden\" &&\n payloadBytes <= this.maxPayloadBytes;\n\n const maxAttempts = Math.max(1, this.retries);\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);\n\n try {\n const res = await this.fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": this.apiKey,\n },\n body: payload,\n signal: controller.signal,\n keepalive: useKeepalive,\n });\n\n if (res.status === 429 || res.status >= 500) {\n throw new RetryableDeliveryError();\n }\n\n if (res.status >= 400 && res.status < 500) {\n this.options.onDeliveryFailed?.({ status: res.status, events });\n this.removeDelivered(events);\n return;\n }\n\n if (!res.ok) {\n throw new RetryableDeliveryError();\n }\n\n return;\n } catch (err) {\n const isLast = attempt >= maxAttempts - 1;\n\n if (isLast) {\n throw err instanceof RetryableDeliveryError\n ? err\n : new RetryableDeliveryError();\n }\n\n await sleep(backoffMs(attempt + 1, this.retryDelay));\n } finally {\n clearTimeout(timeout);\n }\n }\n }\n\n private dropPoisonEvents(events: TrackEvent[]) {\n for (const event of events) {\n this.dropPoisonEvent(event);\n }\n }\n\n private loadAndMergeQueue() {\n const remote = this.storage.get(QUEUE_KEY);\n if (Array.isArray(remote)) {\n this.queue = mergeQueues(this.queue, remote as TrackEvent[]);\n }\n this.trimQueue();\n }\n\n private syncQueue() {\n if (this.persistQueue) {\n const remote = this.storage.get(QUEUE_KEY);\n const merged = mergeQueues(\n Array.isArray(remote) ? (remote as TrackEvent[]) : [],\n this.queue,\n );\n this.queue = this.trimQueue(merged);\n this.storage.set(QUEUE_KEY, this.queue);\n } else {\n this.queue = this.trimQueue();\n }\n this.broadcastQueue();\n }\n\n private trimQueue(queue: TrackEvent[] = this.queue): TrackEvent[] {\n if (queue.length <= this.maxQueue) return queue;\n const dropped = queue.length - this.maxQueue;\n this.options.onEventsDropped?.(dropped);\n return queue.slice(-this.maxQueue);\n }\n\n private broadcastQueue() {\n try {\n this.channel?.postMessage({\n type: \"queue-sync\",\n events: this.queue,\n });\n } catch {\n /* ignore */\n }\n }\n\n private setupQueueSync() {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(CHANNEL_NAME);\n this.channel.onmessage = (e: MessageEvent) => {\n if (e.data?.type !== \"queue-sync\" || !Array.isArray(e.data.events)) return;\n this.queue = mergeQueues(this.queue, e.data.events as TrackEvent[]);\n this.queue = this.trimQueue();\n };\n }\n\n if (this.persistQueue && typeof window !== \"undefined\") {\n this.onStorageQueue = (e: StorageEvent) => {\n if (e.key !== QUEUE_KEY) return;\n this.loadAndMergeQueue();\n };\n window.addEventListener(\"storage\", this.onStorageQueue);\n }\n }\n\n private startTimer() {\n this.timer = setInterval(() => {\n void this.flush();\n }, this.flushInterval);\n }\n\n private setupBrowserExit() {\n const handler = () => {\n if (document.visibilityState === \"hidden\") {\n void this.flush();\n }\n };\n\n const pageHide = () => {\n void this.flush();\n };\n\n window.addEventListener(\"visibilitychange\", handler);\n window.addEventListener(\"pagehide\", pageHide);\n\n this.exitHandlers.push(() => {\n window.removeEventListener(\"visibilitychange\", handler);\n window.removeEventListener(\"pagehide\", pageHide);\n });\n }\n\n private setupProcessExit() {\n const proc = (\n globalThis as {\n process?: {\n on: (event: string, listener: (...args: unknown[]) => void) => void;\n once: (event: string, listener: (...args: unknown[]) => void) => void;\n off?: (event: string, listener: (...args: unknown[]) => void) => void;\n };\n }\n ).process;\n if (!proc?.on) return;\n\n const onSignal = () => {\n void this.shutdown();\n };\n\n proc.once(\"SIGINT\", onSignal);\n proc.once(\"SIGTERM\", onSignal);\n\n this.exitHandlers.push(() => {\n proc.off?.(\"SIGINT\", onSignal);\n proc.off?.(\"SIGTERM\", onSignal);\n });\n }\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eventra_dev/eventra-sdk",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.41",
|
|
4
4
|
"description": "Production-grade analytics SDK for feature usage and product behavior — batching, retries, circuit breaker, runtime-aware (browser, Node, Edge, serverless).",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|