@eventra_dev/eventra-sdk 1.2.41 → 1.2.42

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/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.41";
30
+ var SDK_VERSION = "1.2.42";
31
31
  var LEADER_KEY = "__eventra_leader__";
32
32
  var LEADER_TTL_MS = 4e3;
33
33
  var QUEUE_KEY = "__eventra_q__";
@@ -306,7 +306,7 @@ var Eventra = class {
306
306
  this.apiKey = options.apiKey;
307
307
  this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
308
308
  this.runtime = detectRuntime();
309
- this.fetch = options.fetchImpl ?? globalThis.fetch;
309
+ this.fetch = options.fetchImpl ?? globalThis.fetch?.bind(globalThis);
310
310
  if (!this.fetch) {
311
311
  throw new Error("Eventra: fetch not available \u2014 provide fetchImpl");
312
312
  }
@@ -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\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":[]}
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?.bind(globalThis);\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.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.41";
4
+ var SDK_VERSION = "1.2.42";
5
5
  var LEADER_KEY = "__eventra_leader__";
6
6
  var LEADER_TTL_MS = 4e3;
7
7
  var QUEUE_KEY = "__eventra_q__";
@@ -280,7 +280,7 @@ var Eventra = class {
280
280
  this.apiKey = options.apiKey;
281
281
  this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
282
282
  this.runtime = detectRuntime();
283
- this.fetch = options.fetchImpl ?? globalThis.fetch;
283
+ this.fetch = options.fetchImpl ?? globalThis.fetch?.bind(globalThis);
284
284
  if (!this.fetch) {
285
285
  throw new Error("Eventra: fetch not available \u2014 provide fetchImpl");
286
286
  }
@@ -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\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":[]}
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?.bind(globalThis);\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.41",
3
+ "version": "1.2.42",
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": {
@@ -25,7 +25,8 @@
25
25
  "clean": "rm -rf dist",
26
26
  "test": "vitest run",
27
27
  "test:watch": "vitest",
28
- "test:coverage": "vitest run --coverage"
28
+ "test:coverage": "vitest run --coverage",
29
+ "test:e2e": "pnpm build && playwright test"
29
30
  },
30
31
  "repository": {
31
32
  "type": "git",
@@ -33,6 +34,7 @@
33
34
  },
34
35
  "homepage": "https://eventra.dev",
35
36
  "devDependencies": {
37
+ "@playwright/test": "^1.62.0",
36
38
  "@vitest/coverage-v8": "^4.1.7",
37
39
  "tsup": "^8.0.0",
38
40
  "typescript": "^5.6.0",