/** * Identity Management Module * Handles anonymous_id, user_id, and identity resolution */ export declare class IdentityManager { /** True when this browser continued an in-app browser's visitor (measurement only). */ adoptedFromInAppHandoff: boolean; private anonymousId; private userId; private sessionId; private persistNewId; /** WEB-26: bumps whenever the persisted identity changes, so an in-flight * encrypted write — or read (D01) — can tell it has been superseded. */ private piiGeneration; /** D01: the hydration currently awaiting an encrypted read. It is both the * dedupe handle for concurrent hydrations (one decrypt per page load) and * the handle reset()/invalidateIdentity() drop, so a read that resolves * after a logout or a consent withdrawal cannot land on the new identity. */ private inFlightHydration; constructor(options?: { persistNewId?: boolean; }); /** * Get or create anonymous ID (device/browser identifier) */ private getOrCreateAnonymousId; /** * Read the `_dl_h` handoff token and ALWAYS strip it from the address bar. Returns the * visitor id it carries only in a real browser, when it is ours and still fresh. A webview * never adopts (a webview -> webview share must not merge) and never forwards. */ private consumeInAppHandoff; /** * Persist a freshly-adopted/generated anonymous id to the root-domain cookie + * localStorage. Gated by persistNewId (FSR-107: don't write a tracking identifier for * an opted-out / GPC / DNT visitor at init). */ private persistAnonymousId; /** * TR-15 / FSR-107: tracking became allowed mid-session (optIn / consent grant). Start * persisting AND flush the current in-memory anon id to the cookie + localStorage now. * Without this, a visitor declined at init keeps a memory-only id, so that session's * events land under a visitor_id that vanishes on the next page load (attribution * fragmentation). Idempotent — a no-op once already persisting. */ enablePersistence(): void; /** * Remove a query param from the current URL via history.replaceState (best-effort). * Used so a consumed `_dl_vid` doesn't linger in the address bar and get re-shared. * (FSR-50) */ private stripUrlParam; /** * Set a root domain cookie for cross-subdomain tracking */ private setRootDomainCookie; /** * Get stored user ID from previous session */ private getStoredUserId; /** * WEB-22 — is this user id personally identifying, and therefore not * something we may write to localStorage in the clear? * * The auto-identify path calls `identify(email, { email })`, so the raw * address became the `user_id` and was written unencrypted to `dl_user_id`. * Measured 2026-07-25: on the four workspaces using auto-identify, **every** * event carrying a `user_id` had an email in that column (20,502 / 20,502 on * `f6260736` over 7 days) — while the surrounding code went to real lengths to * encrypt the *same* address in `dl_auto_identified_email`. * * Deliberately narrow: an email is the case that actually occurs and the one * the SDK itself creates. Opaque application ids — the overwhelming majority — * keep the existing fast, synchronous, plaintext path with no behaviour change. */ private static looksLikePII; /** * Persist the user id, encrypting it when it is PII. * * Three-way, and the ordering matters: * - not PII → plaintext `dl_user_id`, exactly as before (sync, always works) * - PII + crypto → encrypted `dl_user_id_pii`, and the plaintext key is REMOVED * - PII, no crypto → memory only, and both keys removed * * That last branch is the deliberate trade. `crypto.subtle` is absent on * http:// and old browsers, and index.ts is explicit that encryption is for * PII-at-rest only and "must NOT gate event delivery". So we never block an * event — `this.userId` is set in memory first and every event still carries * it — we only decline to *persist* an address we cannot protect. The cost is * that a returning visitor on an insecure origin is re-identified by the next * `identify()` call rather than restored from storage; the alternative is * writing their email to disk in the clear, which is what this fixes. */ private persistUserId; /** * Restore a PII user id that was persisted encrypted. Async by necessity, so * it cannot run in the constructor; index.ts calls it during init, right after * `dataEncryption.initialize()`. * * Never overwrites an id already established this page load — an explicit * `identify()` that has already run is fresher than anything on disk. */ hydrateEncryptedUserId(): Promise; /** * Drop the current user WITHOUT rotating the anonymous id. * * The privacy purges (optOut(), setConsent({ analytics: false })) delete the * PII at rest, but deletion alone does not stop an encrypted read that is * already in flight: it resolves afterwards, `this.userId` is still null, and * the hydration restores the very address the visitor just asked us to * forget — every later event then ships their user_id again. Bumping the * generation is what makes that late read a no-op (isSuperseded), so those * paths call this next to their `storage.remove` calls. * * Distinct from reset(): a logout gets a brand-new device id, an opt-out * keeps the visitor's id (there is nothing left to unlink) and only forgets * who they are. */ invalidateIdentity(): void; /** Current identity generation. Bumps on reset() and on every persisted * identity change — callers that await their own identity-derived read * (index.ts's `dl_user_traits` hydration) compare it across the await and * discard the result if it moved. (D01) */ getIdentityGeneration(): number; /** True when `generation` is no longer the live identity — i.e. reset() or an * account change happened while an async read was in flight. */ private isSuperseded; private runHydration; /** * Get the anonymous ID */ getAnonymousId(): string; /** * Get the user ID (if identified) */ getUserId(): string | null; /** * Get the distinct ID (primary identifier) * Returns user_id if identified, otherwise anonymous_id */ getDistinctId(): string; /** * Get canonical ID (alias for distinct_id) */ getCanonicalId(): string; /** * Set the session ID */ setSessionId(sessionId: string): void; /** * Get the session ID */ getSessionId(): string | null; /** * Identify a user * Links anonymous_id to user_id */ identify(userId: string, traits?: Record): Record; /** * Alias one ID to another */ alias(userId: string, previousId?: string): Record; /** * Reset the current user (on logout) * Clears user_id but keeps anonymous_id */ reset(): void; /** * Get all identity fields for event payload */ getIdentityFields(): Record; } //# sourceMappingURL=identity.d.ts.map