@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 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-41%20passing-brightgreen?style=flat-square&logo=vitest&logoColor=white">
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
- 41 vitest tests across 8 suites cover the entire delivery pipeline:
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.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 validateEventName(name) {
75
- const trimmed = name.trim();
76
- if (!trimmed) {
77
- throw new Error("Eventra: event name is required");
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.loadAndMergeQueue();
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
- let eventName;
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 (err) {
356
- const message = err instanceof Error ? err.message : "invalid event";
357
- throw new Error(message);
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
- const remote = this.storage.get(QUEUE_KEY);
558
- const merged = mergeQueues(
559
- Array.isArray(remote) ? remote : [],
560
- this.queue
561
- );
562
- this.queue = this.trimQueue(merged);
563
- this.storage.set(QUEUE_KEY, this.queue);
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();
@@ -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.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 validateEventName(name) {
49
- const trimmed = name.trim();
50
- if (!trimmed) {
51
- throw new Error("Eventra: event name is required");
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.loadAndMergeQueue();
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
- let eventName;
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 (err) {
330
- const message = err instanceof Error ? err.message : "invalid event";
331
- throw new Error(message);
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
- const remote = this.storage.get(QUEUE_KEY);
532
- const merged = mergeQueues(
533
- Array.isArray(remote) ? remote : [],
534
- this.queue
535
- );
536
- this.queue = this.trimQueue(merged);
537
- this.storage.set(QUEUE_KEY, this.queue);
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();
@@ -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":"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","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.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": {