import { IFormoEvent, IFormoEventPayload } from "../types"; import { EnqueueOptions, IEventQueue } from "./type"; type IFormoEventFlushPayload = IFormoEventPayload & { sent_at: string; }; type Options = { apiHost: string; flushAt?: number; flushInterval?: number; host?: string; retryCount?: number; errorHandler?: any; maxQueueSize?: number; canSend?: () => boolean; }; export declare class EventQueue implements IEventQueue { private writeKey; private apiHost; private queue; private timer; private flushAt; private flushIntervalMs; private flushed; private maxQueueSize; private queueByteSize; private errorHandler; private retryCount; private pendingFlush; private payloadHashes; private acceptanceSeq; private lastWall; private wallElapsed; private readonly monotonicStart; private canSend?; private clearSeq; private closed; private disposePageLeave; constructor(writeKey: string, options: Options); private generateMessageId; /** * Fallback fingerprint: the event without its timestamp and without the * volatile generated context. Custom events pass a pre-enrichment * fingerprint instead. */ private generateDedupKey; /** * Drop all queued data and cancel the flush timer. Called on consent * withdrawal / SDK teardown so nothing buffered can be sent later. */ clear(): void; /** Empty the buffer, answering every item's callback with `error`. */ private dropBuffered; /** * Terminal shutdown. Unlike clear(), which only empties the buffer and can * be followed by more events (consent is re-granted, say), close() makes * this queue permanently inert: every later enqueue() is a no-op and the * page-leave listeners are removed. flush() needs no guard of its own, * because a closed queue starts empty and can never be filled again. * * This is the guarantee a torn-down SDK instance needs. Emission decisions * and the emission itself are separated by an await in many call paths * (event creation is async), so guarding the *caller* cannot work: the * instance may be destroyed while the continuation is in flight. Enforcing * it here means no holder of a stale reference can ever send. * * What close() deliberately does NOT stop is a flush already in flight, nor * the buffer it sends first: those events were accepted while the instance * was alive, and dropping them makes every unmount a silent loss. */ close(): void; /** True once close() has run. Exposed for teardown assertions. */ get isClosed(): boolean; enqueue(event: IFormoEvent, callback?: (...args: any) => void, options?: EnqueueOptions): Promise; flush(callback?: (...args: any) => void, drainAll?: boolean): Promise; /** * Returns the UTF-8 byte length of a string. The browser's keepalive limit * is enforced on the wire (UTF-8 bytes), not on JS string length (UTF-16 * code units). Non-ASCII characters (CJK, emoji) can be 2–4x larger in * UTF-8 than their string .length suggests. */ private static byteLength; /** * Splits events into batches that respect the browser's 64KB keepalive * payload size limit. Batches aim for KEEPALIVE_BATCH_TARGET, which leaves * room for a web vitals report; one event alone may use the full limit. Each batch pairs its serialized data with the * original queue items (for per-item callback reporting) and a flag * indicating whether keepalive is safe to use. */ private splitIntoBatches; /** * Sends batches sequentially, notifying per-item callbacks on success/failure. * Returns the first error encountered (if any) so the caller can report it. */ private sendBatches; private isErrorRetryable; /** * Whether an identical event was accepted within the dedup window. Records * the key when it was not. Expired keys are pruned here, on the enqueue * path, so the map is bounded by one minute of accepted events and needs * no timer of its own. */ private isDuplicate; /** * Forget that these events were accepted, so identical ones are taken * again. For items that were never delivered. * * Only an item's OWN entry is released. A send can outlive the window * through retry backoff, or be cut short by clear(); by the time it * fails, the same event may have been accepted again and be in flight * under the same key. The acceptance token tells the two apart. (An * expiry would not: clear() and a re-accept can land in the same * millisecond as the original.) */ private releaseFingerprints; /** * Elapsed time since this queue was created, for the dedup window. Never * decreases, keeps real pace after any clock step, and errs toward * running fast. * * Two sources, the larger wins. The monotonic clock (performance.now) is * immune to wall-clock steps but on some platforms stops while the OS is * suspended, so a device that sleeps mid-window would wake still inside * it. The wall clock counts suspension but can step, so it is read as a * sum of forward deltas: a forward step (or suspension) is added and only * expires entries early, the safe direction for a duplicate guard; a * backward step adds nothing and the sum resumes at real pace from the * new reading. A forward step later corrected backwards therefore leaves * the clock ahead but still advancing, never pinned for the length of * the step. * * Both sources are non-decreasing, so their max is, which is what lets * pruneExpired assume insertion order is expiry order. Per instance, so * test clocks that start from zero are unaffected by other instances. */ private elapsedNow; /** * Drop expired fingerprints from the front of the map. * * Entries are in insertion order and every one expires a fixed interval * after insertion, so the first live entry ends the scan: each expired * entry is visited once in its life, not once per enqueue. The clock never * decreases (see elapsedNow), which is what makes insertion order expiry * order. * * Manual iteration: for..of over a Map does not compile under the ES5 * build target, and Map.forEach cannot stop early. Deleting the current * entry mid-iteration is spec-safe. */ private pruneExpired; /** * Installs the page-leave listeners and returns a disposer that removes * every one of them. Without the disposer each SDK instance leaked five * listeners (three on the global, two on the document) that kept the * instance and its queue alive for the life of the page. */ private onPageLeave; } export {}; //# sourceMappingURL=EventQueue.d.ts.map