UNPKG

webpage-section-tracker

Version:
412 lines (404 loc) 10.6 kB
/** * 브라우저 세션 정보 (탭 수준) */ type BrowserSessionInfo = { id: string; startedAt: string; pageViews: number; isNew: boolean; lastActivityAt?: string; }; /** * 세션 정보 (페이지 수준) */ type ViewSessionInfo = { id: string; parentSessionId: string; startedAt: string; duration: number; engagement: number; interactions?: { clicks: number; scrolls: number; keypresses: number; mouseMoves: number; touches: number; }; maxScrollDepth?: number; }; /** * 애플리케이션 정보 */ type AppInfo = { id: string; version?: string; env?: 'development' | 'staging' | 'production'; }; /** * 페이지 정보 */ type PageInfo = { url: string; title?: string; referrer?: string; path?: string; query?: Record<string, string>; hash?: string; }; /** * 디바이스/브라우저 정보 */ type DeviceInfo = { screen?: { width: number; height: number; pixelRatio?: number; }; viewport?: { width: number; height: number; }; timezone: string; language?: string; userAgent?: string; platform?: string; vendor?: string; }; /** * 사용자 정보 (선택적) */ type UserInfo = { id?: string; isAuthenticated?: boolean; traits?: Record<string, unknown>; }; /** * V2 이벤트 페이로드 - 계층적 구조 */ type EventPayloadV2 = { type: string; timestamp: number; session: { browser: BrowserSessionInfo; view?: ViewSessionInfo; }; environment: { app: AppInfo; page: PageInfo; device: DeviceInfo; }; data?: Record<string, unknown>; user?: UserInfo; custom?: Record<string, unknown>; meta?: { sdkVersion?: string; transport?: 'beacon' | 'fetch' | 'xhr'; attempt?: number; queuedAt?: number; sentAt?: number; }; }; /** * 배치 페이로드 - 여러 이벤트를 한번에 전송 */ type BatchPayloadV2 = { common?: { session?: { browser: BrowserSessionInfo; }; environment?: { app?: AppInfo; device?: DeviceInfo; }; user?: UserInfo; }; events: Array<Omit<EventPayloadV2, 'session' | 'environment' | 'user'> & { session?: Partial<EventPayloadV2['session']>; environment?: Partial<EventPayloadV2['environment']>; user?: Partial<UserInfo>; }>; batchMeta?: { batchId: string; batchSize: number; createdAt: number; sentAt?: number; }; }; type TransportResult = { ok: boolean; status?: number; retryAfterMs?: number; }; type TrackerBaseType = { endpoint: string; appId: string; useBeacon?: boolean; batchSize?: number; flushIntervalMs?: number; maxQueueSize?: number; retryBaseDelayMs?: number; retryMaxDelayMs?: number; sampleRate?: number; getConsent?: () => boolean; context?: Partial<BaseContext>; }; interface TrackerOptionsV1 extends TrackerBaseType { schemaVersion: 'v1'; fetcher?: (url: string, payload: EventPayload) => Promise<TransportResult> | undefined; } interface TrackerOptionsV2 extends TrackerBaseType { schemaVersion: 'v2'; fetcher?: (url: string, payload: BatchPayloadV2) => Promise<TransportResult> | undefined; } type EventPayload = { type: string; ts: number; data?: Record<string, unknown>; ctx?: Partial<BaseContext>; }; type BaseContext = { appId: string; page: string; referrer?: string; tz: string; lang?: string; ua?: string; screen?: { w: number; h: number; dpr?: number; }; sessionId?: string; sessionStart?: string; sessionPageViews?: number; isNewSession?: boolean; viewSessionId?: string; viewSessionStart?: string; viewDuration?: number; viewEngagement?: number; }; declare class Tracker { private queue; private opts; private enabled; private paused; private cleanup; constructor(options: TrackerOptionsV1 | TrackerOptionsV2); pauseFlushing(): void; resumeFlushing(): void; isEnabled(): boolean; setEnabled(flag: boolean): void; track(type: string, data?: any, ctx?: any): void; flush(): Promise<void>; pageView(extra?: Record<string, unknown>): void; identify(userId: string, traits?: Record<string, unknown>): void; destroy(): void; } /** * 3계층 세션 관리 시스템 (v2.0) * * Architecture: * 1. Browser Session (localStorage): 모든 공유, 30 TTL * 2. Page Session (sessionStorage): 탭별 독립, 닫으면 소멸 * 3. View Session (메모리): 페이지 로드별, 새로고침마다 새로 생성 * * 참고: * - Google Analytics Universal Analytics 세션 모델 * - Web Analytics Best Practices * - Multi-tab session isolation patterns */ /** * Browser Session (브라우저 세션) * - 모든 탭에서 공유 * - 30 비활성 만료 * - localStorage에 저장 */ interface BrowserSessionData { browserId: string; firstVisitAt: number; lastActivityAt: number; totalPageViews: number; totalDuration: number; isNew: boolean; } /** * Page Session (페이지 세션) * - 탭별로 독립 * - 닫으면 소멸 * - sessionStorage에 저장 */ interface PageSessionData { pageId: string; browserId: string; openedAt: number; lastActivityAt: number; viewCount: number; isFirstPage: boolean; } /** * View Session (뷰 세션) * - 페이지 로드별로 독립 * - 새로고침, 뒤로가기 매번 새로 생성 * - 메모리에만 저장 */ interface ViewSessionData { viewId: string; pageId: string; browserId: string; loadedAt: number; referrer: string; url: string; isNewView: boolean; } /** * 통합 세션 데이터 (모든 계층 포함) */ interface SessionData { browser: BrowserSessionData; page: PageSessionData; view: ViewSessionData; } /** * 세션 메타데이터 가져오기 (항상 보장) */ declare function getSessionMetadata(): SessionData; declare function touchSession(): void; /** * 페이지뷰 증가 (Browser Session만) */ declare function incrementPageView(): void; /** * View Session 증가 (Page Session) */ declare function incrementViewCount(): void; /** * 세션 초기화 (테스트/개발용) */ declare function resetSession(): void; /** * 세션 ID 단독 조회 (하위 호환성) */ declare function getSessionId(): string; /** * 세션 컨텍스트 객체 생성 (payload용) */ declare function getSessionContext(): Record<string, unknown>; /** * 세션 정보 로깅 (디버깅용) */ declare function logSessionInfo(): void; /** * 세션 정보를 간단한 객체로 반환 (하위 호환성) */ interface LegacySessionData { sessionId: string; startedAt: number; lastActivityAt: number; pageViews: number; isNew: boolean; } /** * 디버깅: 현재 세션 상태 확인 */ declare function getSessionDebugInfo(): { browser: BrowserSessionData | null; page: PageSessionData | null; view: ViewSessionData | null; memoryFallback: { browser: BrowserSessionData | null; page: PageSessionData | null; view: ViewSessionData | null; }; storage: { localStorage: boolean; sessionStorage: boolean; }; }; type DwellTriggerMode = "elementCoverage" | "viewportPosition" | "immediate"; type ElementDwellConfig = { selector: string; trigger: { mode: DwellTriggerMode; value: number; margin?: string; }; throttleMs?: number; allowOversizeFallback?: boolean; observer?: { /** * const root = cfg.rootSelector ? document.querySelector(cfg.rootSelector) : null; * const observer = new IntersectionObserver(callback,{root, threshold: thresholds}); */ rootSelector?: string; rootMargin?: string; thresholds?: number[]; }; heartbeat?: { enabled?: boolean; intervalMs?: number; flushPolicy?: "batch" | "debounce" | "immediate"; debounceMs?: number; adaptive?: { enabled?: boolean; baseMs?: number; maxMs?: number; factor?: number; resetOnScroll?: boolean; scrollDebounceMs?: number; }; }; meta?: { collectStableSelector?: boolean; collectNthPath?: boolean; collectDataAttrs?: boolean; dataAttrAllowlist?: string[]; maxDataAttrs?: number; }; initialReport?: "none" | "progress" | "snapshot" | "guarded"; initialGuard?: { minStableMs?: number; afterInteraction?: boolean; initialMinCoverage?: number; rafPasses?: number; }; }; interface ElementDwellSnapshot { selector: string; tag: string; id?: string; className?: string; stableSelector?: string; nthPath?: string; dataAttrs?: Record<string, string>; elementSize: { w: number; h: number; }; viewport: { w: number; h: number; dpr?: number; }; firstVisibleAtMs?: number; trackingStartedAtMs: number; collectedAtMs: number; dwellMs: number; visibleNow: boolean; pageVisible: boolean; windowFocused: boolean; basis: DwellTriggerMode; elementCoverage: number; viewportTopPct: number; viewportBottomPct?: number; viewportCoverage?: number; isOversized?: boolean; visibleHeightPx?: number; } interface ElementDwellController { stop(): void; getSnapshots(): ElementDwellSnapshot[]; onChange(callback: (snapshots: ElementDwellSnapshot[]) => void): () => void; } declare function monitorElementDwell(tracker: Tracker, configs: ElementDwellConfig[]): ElementDwellController; declare function createTracker(opts: TrackerOptionsV1 | TrackerOptionsV2): any; export { type BaseContext, type BrowserSessionData, type DwellTriggerMode, type ElementDwellConfig, type ElementDwellController, type ElementDwellSnapshot, type EventPayload, type LegacySessionData, type PageSessionData, type SessionData, Tracker, type TrackerOptionsV1, type TrackerOptionsV2, type ViewSessionData, createTracker, getSessionContext, getSessionDebugInfo, getSessionId, getSessionMetadata, incrementPageView, incrementViewCount, logSessionInfo, monitorElementDwell, resetSession, touchSession };