1import { BridgeComponent } from "@hotwired/hotwire-native-bridge" 2import { 3 allowedEvent, 4 buildInstallAttributionBody 5} from "controllers/bridge/analytics_bridge_policy" 6 7const RECORD_TIMEOUT_MS = 8000 8 9export default class extends BridgeComponent { 10 static component = "analytics" 11 static values = { event: String, recordUrl: String } 12 13 connect() { 14 super.connect() 15 if (!this.enabled) return 16 17 try { 18 this.send("connect", {}, (reply) => this.handleConnect(reply)) 19 if (allowedEvent(this.eventValue)) { 20 this.send("log-event", { name: this.eventValue }, () => {}) 21 } 22 } catch (error) { 23 console.debug("Analytics bridge connection failed", error) 24 } 25 } 26 27 handleConnect(message) { 28 const data = message?.data 29 if (typeof data?.install_referrer !== "string" || data.install_referrer.trim() === "") return 30 31 this.recordInstallAttribution(data).catch((error) => { 32 console.debug("Install attribution recording failed", error) 33 }) 34 } 35 36 async recordInstallAttribution(data) { 37 const controller = new AbortController() 38 const timeout = setTimeout(() => controller.abort(), RECORD_TIMEOUT_MS) 39 timeout.unref?.() 40 41 try { 42 const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content || "" 43 const response = await fetch(this.recordUrlValue, { 44 method: "POST", 45 headers: { 46 "Content-Type": "application/json", 47 "X-CSRF-Token": csrfToken, 48 "Accept": "application/json" 49 }, 50 credentials: "same-origin", 51 body: JSON.stringify(buildInstallAttributionBody(data, navigator.userAgent)), 52 signal: controller.signal 53 }) 54 if (!response.ok) return 55 56 this.send( 57 "install-referrer-recorded", 58 { installation_id: data.installation_id ?? null }, 59 () => {} 60 ) 61 } finally { 62 clearTimeout(timeout) 63 } 64 } 65}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.