@behindthescenes/analytics 0.0.1

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/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # `@behindthescenes/analytics`
2
+
3
+ Browser SDK for BTS external-site analytics.
4
+
5
+ ## What it does
6
+
7
+ - Records standard web events such as `page_view`, `view_content`, `lead`, `sign_up`, `begin_checkout`, and `purchase`
8
+ - Auto-tracks pageviews, SPA history changes, outbound clicks, button clicks, and form submissions by default
9
+ - Supports custom client-side events with `track(...)`
10
+ - Preserves journey tokens, UTM params, and click IDs for downstream attribution and CAPI delivery
11
+
12
+ ## Install from npm
13
+
14
+ ```bash
15
+ npm install @behindthescenes/analytics
16
+ ```
17
+
18
+ ```bash
19
+ pnpm add @behindthescenes/analytics
20
+ ```
21
+
22
+ ```bash
23
+ yarn add @behindthescenes/analytics
24
+ ```
25
+
26
+ ```bash
27
+ bun add @behindthescenes/analytics
28
+ ```
29
+
30
+ ```ts
31
+ import { createBTSAnalytics } from "@behindthescenes/analytics";
32
+
33
+ const analytics = createBTSAnalytics({
34
+ siteKey: "your-public-site-key",
35
+ endpoint: "https://api.bts.dev/v2/website/analytics",
36
+ });
37
+
38
+ analytics.track("video_played", {
39
+ videoId: "intro-01",
40
+ });
41
+ ```
42
+
43
+ ## BTS-hosted browser bundle
44
+
45
+ ```html
46
+ <script type="module">
47
+ import { createBTSAnalytics } from "https://app.behindthescenes.com/sdk/analytics/latest/browser/browser.js";
48
+
49
+ window.btsAnalytics = createBTSAnalytics({
50
+ siteKey: "your-public-site-key",
51
+ endpoint: "https://api.bts.dev/v2/website/analytics"
52
+ });
53
+ </script>
54
+ ```
55
+
56
+ The hosted bundle is generated from `packages/analytics/dist` and synced into `apps/bts-web/public/sdk/analytics`.
57
+
58
+ ## Standard events
59
+
60
+ ```ts
61
+ analytics.trackStandard("view_content", {
62
+ contentId: "offer-42",
63
+ });
64
+
65
+ analytics.trackStandard("purchase", {
66
+ currency: "USD",
67
+ monetaryValue: 149,
68
+ orderId: "order_123",
69
+ });
70
+ ```
71
+
72
+ Supported standard events:
73
+
74
+ - `page_view`
75
+ - `view_content`
76
+ - `search`
77
+ - `lead`
78
+ - `sign_up`
79
+ - `begin_checkout`
80
+ - `add_payment_info`
81
+ - `purchase`
82
+ - `outbound_click`
83
+ - `button_click`
84
+ - `form_submit`
85
+
86
+ Legacy aliases such as `lead_capture`, `registration_complete`, `checkout_started`, and `purchase_completed` are normalized into the standard conversion catalog.
87
+
88
+ ## Custom events
89
+
90
+ ```ts
91
+ analytics.track("cta_clicked", {
92
+ ctaId: "hero-primary",
93
+ placement: "hero",
94
+ });
95
+ ```
96
+
97
+ ## Journeys
98
+
99
+ ```ts
100
+ const { journeyToken } = await analytics.startFunnel();
101
+ const checkoutUrl = analytics.decorateUrl("https://behindthescenes.com/checkout", journeyToken);
102
+ ```
103
+
104
+ ## Scripts
105
+
106
+ ```bash
107
+ bun run build
108
+ bun run build:hosted
109
+ bun run test
110
+ ```
111
+
112
+ `build:hosted` rebuilds the package and syncs the hosted browser bundle into `apps/bts-web/public/sdk/analytics`.
113
+
114
+ ## Release
115
+
116
+ ```bash
117
+ bun run release:check
118
+ ```
119
+
120
+ 1. Bump the version in `packages/analytics/package.json`.
121
+ 2. For a local/manual publish, run `bun run release:publish`.
122
+ 3. For GitHub Actions, push a tag named `analytics-sdk-v<version>` or run the `Analytics SDK Release` workflow manually.
123
+ 4. The GitHub Actions release workflow publishes with provenance; local publishes should use `npm publish --access public` without `--provenance`.
124
+
@@ -0,0 +1,4 @@
1
+ var M=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],G=new Set(M),P=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),T={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function w(B){return G.has(B)}function K(B){let c=B.trim();if(c.length===0)return{eventName:c,canonicalEventName:c,isStandard:!1};let _=c.toLowerCase(),E=T[_];if(E)return{eventName:E,canonicalEventName:E,aliasOf:E,isStandard:!0};return{eventName:c,canonicalEventName:c,isStandard:w(c)}}function F(B,c){if(c)return c;if(B==="page_view")return"page_view";if(B==="identify")return"identify";if(P.has(B))return"conversion";return"custom"}function $(){return[...M]}var H="bts_analytics_vid",J="bts_analytics_sid",Q="bts_analytics_jt",k="bts_analytics_attr",z="bts_site",v="bts_jt",I=300,U={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0},p=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"];function A(){return typeof window>"u"?null:window}function f(){return typeof document>"u"?null:document}function N(){return A()?.localStorage??null}function W(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function q(B){return B!==null&&typeof B==="object"&&!Array.isArray(B)}function O(B){if(!B)return null;try{let c=JSON.parse(B);if(!q(c))return null;let _=q(c.utm)?Object.fromEntries(Object.entries(c.utm).filter((S)=>typeof S[1]==="string")):{},E=q(c.clickIds)?Object.fromEntries(Object.entries(c.clickIds).filter((S)=>typeof S[1]==="string")):{};return{utm:_,clickIds:E,landingUrl:typeof c.landingUrl==="string"?c.landingUrl:void 0,lastSeenAt:typeof c.lastSeenAt==="string"?c.lastSeenAt:new Date().toISOString(),referrer:typeof c.referrer==="string"?c.referrer:void 0}}catch{return null}}function j(){let B=N();return O(B?.getItem(k)??null)}function d(B){let c=N();if(!c)return;c.setItem(k,JSON.stringify(B))}function D(){let B=A();if(!B)return;return`${B.location.pathname}${B.location.search}`}function C(){return A()?.location.href}function X(){return A()?.location.origin??"https://behindthescenes.com"}function Y(){return f()?.referrer||void 0}function Z(){let B=A();if(!B)return j();let c=new URLSearchParams(B.location.search),_={utm:{},clickIds:{},landingUrl:B.location.href,lastSeenAt:new Date().toISOString(),referrer:Y()};for(let y of p){let V=c.get(y);if(!V)continue;if(y.startsWith("utm_")){_.utm[y]=V;continue}_.clickIds[y]=V}let E=j(),S={utm:{...E?.utm??{},..._.utm},clickIds:{...E?.clickIds??{},..._.clickIds},landingUrl:_.landingUrl??E?.landingUrl,lastSeenAt:_.lastSeenAt,referrer:_.referrer??E?.referrer};return d(S),S}function l(B){let c=B?.trim();return c?c.slice(0,512):void 0}function g(B){let c={...U};if(B.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1};if(B.autoTrack&&typeof B.autoTrack==="object")return{pageviews:B.autoTrack.pageviews??c.pageviews,history:B.autoTrack.history??c.history,outboundLinks:B.autoTrack.outboundLinks??c.outboundLinks,buttonClicks:B.autoTrack.buttonClicks??c.buttonClicks,formSubmissions:B.autoTrack.formSubmissions??c.formSubmissions};if(B.autoPageviews===!1)return{...c,pageviews:!1};if(B.autoPageviews===!0)return{...c,pageviews:!0};return c}class m{siteKey;endpoint;debug;flushIntervalMs;autoTrack;queue=[];flushTimer=null;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;constructor(B){if(this.siteKey=B.siteKey,this.endpoint=B.endpoint.replace(/\/$/,""),this.debug=B.debug??!1,this.flushIntervalMs=B.flushIntervalMs??I,this.autoTrack=g(B),Z(),A())this.installAutoTracking()}static init(B){return new m(B)}getVisitorId(){let B=N(),c=B?.getItem(H);if(c)return c;let _=W();return B?.setItem(H,_),_}getSessionId(){let B=N(),c=B?.getItem(J);if(c)return c;let _=W();return B?.setItem(J,_),_}log(...B){if(this.debug)console.log("[@bts/analytics]",...B)}installAutoTracking(){let B=A(),c=f();if(!B||!c)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(B),this.popstateHandler=()=>{this.recordPageView()},B.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(_)=>{this.handleAutoClick(_)},this.submitHandler=(_)=>{this.handleAutoSubmit(_)},this.pagehideHandler=()=>{this.flushWithBeacon()},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)c.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions)c.addEventListener("submit",this.submitHandler,!0);B.addEventListener("pagehide",this.pagehideHandler)}patchHistory(B){let c=B.history,_=c.pushState.bind(c),E=c.replaceState.bind(c),S=()=>{Z(),this.recordPageView()};return c.pushState=(...y)=>{_(...y),S()},c.replaceState=(...y)=>{E(...y),S()},()=>{c.pushState=_,c.replaceState=E}}handleAutoClick(B){if(B.defaultPrevented)return;let c=B.target;if(!(c instanceof Element))return;if(this.autoTrack.outboundLinks){let E=c.closest("a[href]");if(E instanceof HTMLAnchorElement){let S=l(E.href);if(!S)return;let y=new URL(S,X());if(y.origin!==X()){this.trackStandard("outbound_click",{elementHref:S,elementId:l(E.id),elementText:l(E.textContent),linkHost:y.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let _=c.closest("button,[role='button'],[data-bts-track-click]");if(!(_ instanceof Element))return;this.trackStandard("button_click",{elementId:l(_.id),elementName:l(_.getAttribute("name")),elementRole:l(_.getAttribute("role")),elementText:l(_.textContent)})}handleAutoSubmit(B){if(B.defaultPrevented||!this.autoTrack.formSubmissions)return;let c=B.target;if(!(c instanceof HTMLFormElement))return;this.trackStandard("form_submit",{formAction:l(c.action),formId:l(c.id),formMethod:l(c.method),formName:l(c.getAttribute("name"))})}buildEvent(B,c,_,E){let S=Z(),y=E?.path??D(),V=Y();return{eventName:B,eventType:c,path:y,referrer:V,occurredAt:new Date().toISOString(),visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{..._??{},attribution:S?{utm:S.utm,clickIds:S.clickIds,landingUrl:S.landingUrl,referrer:S.referrer}:void 0,page:{title:f()?.title,url:C()}}}}queueEvent(B,c,_){let E=K(B),S=F(E.canonicalEventName,_),y={...c??{}};if(E.aliasOf)y.originalEventName=B;if(this.queue.push(this.buildEvent(E.eventName,S,y)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(B,c){let _=`${this.endpoint}${B}`;return this.log("POST",_,c),fetch(_,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(c)})}flushWithBeacon(){if(this.queue.length===0)return;let B=A(),c=B?.navigator?.sendBeacon?.bind(B.navigator);if(!c)return;let _=[...this.queue];this.queue=[];let E=`${this.endpoint}/ingest/batch`,S=JSON.stringify({siteKey:this.siteKey,events:_}),y=new Blob([S],{type:"application/json"});c(E,y)}async flushNow(){if(this.queue.length===0)return;let B=[...this.queue];this.queue=[];try{let c=await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:B});if(!c.ok)this.log("flush failed",c.status)}catch(c){this.log("flush error",c)}}recordPageView(B){let c=B??D(),_=C()??c??"/";if(_===this.lastTrackedUrl)return;this.lastTrackedUrl=_,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:c})),this.scheduleFlush()}page(B){this.lastTrackedUrl=null,this.recordPageView(B)}track(B,c){this.queueEvent(B,c)}trackStandard(B,c){this.queueEvent(B,c)}identify(B,c){this.queue.push(this.buildEvent("identify","identify",{traits:c??{},userId:B})),this.scheduleFlush()}listStandardEvents(){return $()}async startFunnel(B){let c=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:B??D()});if(!c.ok)throw Error(`journey/start failed: ${c.status}`);let _=await c.json();return N()?.setItem(Q,_.journeyToken),_}getPersistedJourneyToken(){return N()?.getItem(Q)??null}decorateUrl(B,c){let _=c??this.getPersistedJourneyToken(),E=new URL(B,X());if(E.searchParams.set(z,this.siteKey),_)E.searchParams.set(v,_);return E.toString()}async notifyHandoff(B,c){let _=await this.postJson("/journey/handoff",{journeyToken:B,context:c});if(!_.ok)throw Error(`journey/handoff failed: ${_.status}`)}destroy(){let B=A(),c=f();if(c&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))c.removeEventListener("click",this.clickHandler,!0);if(c&&this.submitHandler&&this.autoTrack.formSubmissions)c.removeEventListener("submit",this.submitHandler,!0);if(B&&this.popstateHandler)B.removeEventListener("popstate",this.popstateHandler);if(B&&this.pagehideHandler)B.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function L(B){return m.init(B)}if(typeof window<"u")window.BTSAnalytics={BTSAnalytics:m,createBTSAnalytics:L,listStandardWebEvents:$},window.createBTSAnalytics=L;export{$ as listStandardWebEvents,L as createBTSAnalytics,m as BTSAnalytics};
2
+
3
+ //# debugId=8CE5844CD6E1239864756E2164756E21
4
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/events.ts", "../../src/index.ts", "../../src/browser.ts"],
4
+ "sourcesContent": [
5
+ "export const STANDARD_WEB_EVENTS = [\n \"page_view\",\n \"view_content\",\n \"search\",\n \"lead\",\n \"sign_up\",\n \"begin_checkout\",\n \"add_payment_info\",\n \"purchase\",\n \"outbound_click\",\n \"button_click\",\n \"form_submit\",\n] as const;\n\nexport type BTSAnalyticsStandardEventName = (typeof STANDARD_WEB_EVENTS)[number];\n\nexport type BTSAnalyticsEventType = \"page_view\" | \"custom\" | \"identify\" | \"conversion\";\n\nconst STANDARD_EVENT_SET = new Set<string>(STANDARD_WEB_EVENTS);\n\nconst CONVERSION_EVENTS = new Set<string>([\"lead\", \"sign_up\", \"begin_checkout\", \"add_payment_info\", \"purchase\"]);\n\nconst LEGACY_EVENT_ALIASES: Record<string, BTSAnalyticsStandardEventName> = {\n $pageview: \"page_view\",\n checkout_started: \"begin_checkout\",\n lead_capture: \"lead\",\n purchase_completed: \"purchase\",\n registration_complete: \"sign_up\",\n};\n\nexport function isStandardWebEventName(eventName: string): eventName is BTSAnalyticsStandardEventName {\n return STANDARD_EVENT_SET.has(eventName);\n}\n\nexport function normalizeEventName(eventName: string): {\n eventName: string;\n canonicalEventName: string;\n aliasOf?: BTSAnalyticsStandardEventName;\n isStandard: boolean;\n} {\n const trimmed = eventName.trim();\n if (trimmed.length === 0) {\n return {\n eventName: trimmed,\n canonicalEventName: trimmed,\n isStandard: false,\n };\n }\n\n const lowered = trimmed.toLowerCase();\n const aliasOf = LEGACY_EVENT_ALIASES[lowered];\n if (aliasOf) {\n return {\n eventName: aliasOf,\n canonicalEventName: aliasOf,\n aliasOf,\n isStandard: true,\n };\n }\n\n return {\n eventName: trimmed,\n canonicalEventName: trimmed,\n isStandard: isStandardWebEventName(trimmed),\n };\n}\n\nexport function resolveEventType(eventName: string, explicitType?: BTSAnalyticsEventType): BTSAnalyticsEventType {\n if (explicitType) {\n return explicitType;\n }\n if (eventName === \"page_view\") {\n return \"page_view\";\n }\n if (eventName === \"identify\") {\n return \"identify\";\n }\n if (CONVERSION_EVENTS.has(eventName)) {\n return \"conversion\";\n }\n return \"custom\";\n}\n\nexport function listStandardWebEvents(): BTSAnalyticsStandardEventName[] {\n return [...STANDARD_WEB_EVENTS];\n}\n",
6
+ "import {\n listStandardWebEvents,\n normalizeEventName,\n resolveEventType,\n type BTSAnalyticsEventType,\n type BTSAnalyticsStandardEventName,\n} from \"./events\";\n\nconst STORAGE_VISITOR = \"bts_analytics_vid\";\nconst STORAGE_SESSION = \"bts_analytics_sid\";\nconst STORAGE_JOURNEY = \"bts_analytics_jt\";\nconst STORAGE_ATTRIBUTION = \"bts_analytics_attr\";\nconst QUERY_SITE = \"bts_site\";\nconst QUERY_JOURNEY = \"bts_jt\";\n\nconst DEFAULT_FLUSH_INTERVAL_MS = 300;\n\nconst DEFAULT_AUTO_TRACK = {\n pageviews: true,\n history: true,\n outboundLinks: true,\n buttonClicks: true,\n formSubmissions: true,\n} as const;\n\nconst ATTRIBUTION_QUERY_KEYS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_term\",\n \"utm_content\",\n \"fbclid\",\n \"gclid\",\n \"gbraid\",\n \"li_fat_id\",\n \"msclkid\",\n \"ttclid\",\n \"wbraid\",\n] as const;\n\ntype AutoTrackConfig = {\n pageviews: boolean;\n history: boolean;\n outboundLinks: boolean;\n buttonClicks: boolean;\n formSubmissions: boolean;\n};\n\nexport type BTSAnalyticsInit = {\n siteKey: string;\n endpoint: string;\n autoPageviews?: boolean;\n autoTrack?: boolean | Partial<AutoTrackConfig>;\n debug?: boolean;\n flushIntervalMs?: number;\n};\n\nexport type BTSAnalyticsPayloadProperties = Record<string, unknown>;\n\ntype StoredAttribution = {\n utm: Record<string, string>;\n clickIds: Record<string, string>;\n landingUrl?: string;\n lastSeenAt: string;\n referrer?: string;\n};\n\ntype QueuedAnalyticsEvent = {\n eventName: string;\n eventType: BTSAnalyticsEventType;\n path?: string;\n referrer?: string;\n occurredAt: string;\n visitorId: string;\n sessionId: string;\n properties: BTSAnalyticsPayloadProperties;\n};\n\nfunction safeWindow(): Window | null {\n return typeof window === \"undefined\" ? null : window;\n}\n\nfunction safeDocument(): Document | null {\n return typeof document === \"undefined\" ? null : document;\n}\n\nfunction safeStorage(): Storage | null {\n const win = safeWindow();\n return win?.localStorage ?? null;\n}\n\nfunction randomId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n return `v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction parseStoredAttribution(raw: string | null): StoredAttribution | null {\n if (!raw) {\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!isRecord(parsed)) {\n return null;\n }\n const utm = isRecord(parsed.utm)\n ? Object.fromEntries(Object.entries(parsed.utm).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"))\n : {};\n const clickIds = isRecord(parsed.clickIds)\n ? Object.fromEntries(\n Object.entries(parsed.clickIds).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"),\n )\n : {};\n return {\n utm,\n clickIds,\n landingUrl: typeof parsed.landingUrl === \"string\" ? parsed.landingUrl : undefined,\n lastSeenAt: typeof parsed.lastSeenAt === \"string\" ? parsed.lastSeenAt : new Date().toISOString(),\n referrer: typeof parsed.referrer === \"string\" ? parsed.referrer : undefined,\n };\n } catch {\n return null;\n }\n}\n\nfunction readStoredAttribution(): StoredAttribution | null {\n const storage = safeStorage();\n return parseStoredAttribution(storage?.getItem(STORAGE_ATTRIBUTION) ?? null);\n}\n\nfunction writeStoredAttribution(next: StoredAttribution): void {\n const storage = safeStorage();\n if (!storage) {\n return;\n }\n storage.setItem(STORAGE_ATTRIBUTION, JSON.stringify(next));\n}\n\nfunction currentPath(): string | undefined {\n const win = safeWindow();\n if (!win) {\n return undefined;\n }\n return `${win.location.pathname}${win.location.search}`;\n}\n\nfunction currentUrl(): string | undefined {\n return safeWindow()?.location.href;\n}\n\nfunction currentOrigin(): string {\n return safeWindow()?.location.origin ?? \"https://behindthescenes.com\";\n}\n\nfunction currentReferrer(): string | undefined {\n return safeDocument()?.referrer || undefined;\n}\n\nfunction readCurrentAttribution(): StoredAttribution | null {\n const win = safeWindow();\n if (!win) {\n return readStoredAttribution();\n }\n const params = new URLSearchParams(win.location.search);\n const next: StoredAttribution = {\n utm: {},\n clickIds: {},\n landingUrl: win.location.href,\n lastSeenAt: new Date().toISOString(),\n referrer: currentReferrer(),\n };\n for (const key of ATTRIBUTION_QUERY_KEYS) {\n const value = params.get(key);\n if (!value) {\n continue;\n }\n if (key.startsWith(\"utm_\")) {\n next.utm[key] = value;\n continue;\n }\n next.clickIds[key] = value;\n }\n\n const previous = readStoredAttribution();\n const merged: StoredAttribution = {\n utm: { ...(previous?.utm ?? {}), ...next.utm },\n clickIds: { ...(previous?.clickIds ?? {}), ...next.clickIds },\n landingUrl: next.landingUrl ?? previous?.landingUrl,\n lastSeenAt: next.lastSeenAt,\n referrer: next.referrer ?? previous?.referrer,\n };\n writeStoredAttribution(merged);\n return merged;\n}\n\nfunction sanitizeText(value: string | null | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed.slice(0, 512) : undefined;\n}\n\nfunction createAutoTrackConfig(init: BTSAnalyticsInit): AutoTrackConfig {\n const base = { ...DEFAULT_AUTO_TRACK };\n if (init.autoTrack === false) {\n return {\n pageviews: false,\n history: false,\n outboundLinks: false,\n buttonClicks: false,\n formSubmissions: false,\n };\n }\n if (init.autoTrack && typeof init.autoTrack === \"object\") {\n return {\n pageviews: init.autoTrack.pageviews ?? base.pageviews,\n history: init.autoTrack.history ?? base.history,\n outboundLinks: init.autoTrack.outboundLinks ?? base.outboundLinks,\n buttonClicks: init.autoTrack.buttonClicks ?? base.buttonClicks,\n formSubmissions: init.autoTrack.formSubmissions ?? base.formSubmissions,\n };\n }\n if (init.autoPageviews === false) {\n return {\n ...base,\n pageviews: false,\n }\n }\n if (init.autoPageviews === true) {\n return {\n ...base,\n pageviews: true,\n }\n }\n return base;\n}\n\nexport class BTSAnalytics {\n private siteKey: string;\n private endpoint: string;\n private debug: boolean;\n private flushIntervalMs: number;\n private autoTrack: AutoTrackConfig;\n private queue: QueuedAnalyticsEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private lastTrackedUrl: string | null = null;\n private unpatchHistory: (() => void) | null = null;\n private clickHandler: ((event: Event) => void) | null = null;\n private submitHandler: ((event: Event) => void) | null = null;\n private pagehideHandler: (() => void) | null = null;\n private popstateHandler: (() => void) | null = null;\n\n constructor(init: BTSAnalyticsInit) {\n this.siteKey = init.siteKey;\n this.endpoint = init.endpoint.replace(/\\/$/, \"\");\n this.debug = init.debug ?? false;\n this.flushIntervalMs = init.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.autoTrack = createAutoTrackConfig(init);\n\n readCurrentAttribution();\n\n if (safeWindow()) {\n this.installAutoTracking();\n }\n }\n\n static init(opts: BTSAnalyticsInit): BTSAnalytics {\n return new BTSAnalytics(opts);\n }\n\n getVisitorId(): string {\n const storage = safeStorage();\n const existing = storage?.getItem(STORAGE_VISITOR);\n if (existing) {\n return existing;\n }\n const id = randomId();\n storage?.setItem(STORAGE_VISITOR, id);\n return id;\n }\n\n getSessionId(): string {\n const storage = safeStorage();\n const existing = storage?.getItem(STORAGE_SESSION);\n if (existing) {\n return existing;\n }\n const id = randomId();\n storage?.setItem(STORAGE_SESSION, id);\n return id;\n }\n\n private log(...args: unknown[]) {\n if (this.debug) {\n // eslint-disable-next-line no-console\n console.log(\"[@bts/analytics]\", ...args);\n }\n }\n\n private installAutoTracking(): void {\n const win = safeWindow();\n const doc = safeDocument();\n if (!win || !doc) {\n return;\n }\n\n if (this.autoTrack.pageviews) {\n this.recordPageView();\n }\n\n if (this.autoTrack.history) {\n this.unpatchHistory = this.patchHistory(win);\n this.popstateHandler = () => {\n this.recordPageView();\n };\n win.addEventListener(\"popstate\", this.popstateHandler);\n }\n\n this.clickHandler = (event: Event) => {\n this.handleAutoClick(event);\n };\n this.submitHandler = (event: Event) => {\n this.handleAutoSubmit(event);\n };\n this.pagehideHandler = () => {\n this.flushWithBeacon();\n };\n\n if (this.autoTrack.outboundLinks || this.autoTrack.buttonClicks) {\n doc.addEventListener(\"click\", this.clickHandler, true);\n }\n if (this.autoTrack.formSubmissions) {\n doc.addEventListener(\"submit\", this.submitHandler, true);\n }\n win.addEventListener(\"pagehide\", this.pagehideHandler);\n }\n\n private patchHistory(win: Window): () => void {\n const history = win.history;\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n const onHistoryChange = () => {\n readCurrentAttribution();\n this.recordPageView();\n };\n\n history.pushState = ((...args: Parameters<History[\"pushState\"]>) => {\n originalPushState(...args);\n onHistoryChange();\n }) as History[\"pushState\"];\n\n history.replaceState = ((...args: Parameters<History[\"replaceState\"]>) => {\n originalReplaceState(...args);\n onHistoryChange();\n }) as History[\"replaceState\"];\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n };\n }\n\n private handleAutoClick(event: Event): void {\n if (event.defaultPrevented) {\n return;\n }\n const target = event.target;\n if (!(target instanceof Element)) {\n return;\n }\n\n if (this.autoTrack.outboundLinks) {\n const anchor = target.closest(\"a[href]\");\n if (anchor instanceof HTMLAnchorElement) {\n const href = sanitizeText(anchor.href);\n if (!href) {\n return;\n }\n const destination = new URL(href, currentOrigin());\n if (destination.origin !== currentOrigin()) {\n this.trackStandard(\"outbound_click\", {\n elementHref: href,\n elementId: sanitizeText(anchor.id),\n elementText: sanitizeText(anchor.textContent),\n linkHost: destination.hostname,\n });\n return;\n }\n }\n }\n\n if (!this.autoTrack.buttonClicks) {\n return;\n }\n\n const button = target.closest(\"button,[role='button'],[data-bts-track-click]\");\n if (!(button instanceof Element)) {\n return;\n }\n this.trackStandard(\"button_click\", {\n elementId: sanitizeText(button.id),\n elementName: sanitizeText(button.getAttribute(\"name\")),\n elementRole: sanitizeText(button.getAttribute(\"role\")),\n elementText: sanitizeText(button.textContent),\n });\n }\n\n private handleAutoSubmit(event: Event): void {\n if (event.defaultPrevented || !this.autoTrack.formSubmissions) {\n return;\n }\n const target = event.target;\n if (!(target instanceof HTMLFormElement)) {\n return;\n }\n this.trackStandard(\"form_submit\", {\n formAction: sanitizeText(target.action),\n formId: sanitizeText(target.id),\n formMethod: sanitizeText(target.method),\n formName: sanitizeText(target.getAttribute(\"name\")),\n });\n }\n\n private buildEvent(\n eventName: string,\n eventType: BTSAnalyticsEventType,\n properties?: BTSAnalyticsPayloadProperties,\n overrides?: { path?: string },\n ): QueuedAnalyticsEvent {\n const attribution = readCurrentAttribution();\n const path = overrides?.path ?? currentPath();\n const referrer = currentReferrer();\n return {\n eventName,\n eventType,\n path,\n referrer,\n occurredAt: new Date().toISOString(),\n visitorId: this.getVisitorId(),\n sessionId: this.getSessionId(),\n properties: {\n ...(properties ?? {}),\n attribution: attribution\n ? {\n utm: attribution.utm,\n clickIds: attribution.clickIds,\n landingUrl: attribution.landingUrl,\n referrer: attribution.referrer,\n }\n : undefined,\n page: {\n title: safeDocument()?.title,\n url: currentUrl(),\n },\n },\n };\n }\n\n private queueEvent(eventName: string, properties?: BTSAnalyticsPayloadProperties, explicitType?: BTSAnalyticsEventType): void {\n const normalized = normalizeEventName(eventName);\n const eventType = resolveEventType(normalized.canonicalEventName, explicitType);\n const nextProperties: BTSAnalyticsPayloadProperties = {\n ...(properties ?? {}),\n };\n if (normalized.aliasOf) {\n nextProperties.originalEventName = eventName;\n }\n this.queue.push(this.buildEvent(normalized.eventName, eventType, nextProperties));\n if (this.queue.length >= 50) {\n void this.flushNow();\n return;\n }\n this.scheduleFlush();\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer) {\n return;\n }\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n void this.flushNow();\n }, this.flushIntervalMs);\n }\n\n private async postJson(path: string, body: unknown): Promise<Response> {\n const url = `${this.endpoint}${path}`;\n this.log(\"POST\", url, body);\n return fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n }\n\n private flushWithBeacon(): void {\n if (this.queue.length === 0) {\n return;\n }\n const win = safeWindow();\n const beacon = win?.navigator?.sendBeacon?.bind(win.navigator);\n if (!beacon) {\n return;\n }\n const batch = [...this.queue];\n this.queue = [];\n const url = `${this.endpoint}/ingest/batch`;\n const body = JSON.stringify({ siteKey: this.siteKey, events: batch });\n const blob = new Blob([body], { type: \"application/json\" });\n beacon(url, blob);\n }\n\n async flushNow(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n const batch = [...this.queue];\n this.queue = [];\n try {\n const response = await this.postJson(\"/ingest/batch\", {\n siteKey: this.siteKey,\n events: batch,\n });\n if (!response.ok) {\n this.log(\"flush failed\", response.status);\n }\n } catch (error) {\n this.log(\"flush error\", error);\n }\n }\n\n private recordPageView(path?: string): void {\n const derivedPath = path ?? currentPath();\n const url = currentUrl() ?? derivedPath ?? \"/\";\n if (url === this.lastTrackedUrl) {\n return;\n }\n this.lastTrackedUrl = url;\n this.queue.push(\n this.buildEvent(\n \"page_view\",\n \"page_view\",\n {\n autoCaptured: true,\n },\n { path: derivedPath },\n ),\n );\n this.scheduleFlush();\n }\n\n page(path?: string): void {\n this.lastTrackedUrl = null;\n this.recordPageView(path);\n }\n\n track(eventName: string, properties?: BTSAnalyticsPayloadProperties): void {\n this.queueEvent(eventName, properties);\n }\n\n trackStandard(eventName: BTSAnalyticsStandardEventName, properties?: BTSAnalyticsPayloadProperties): void {\n this.queueEvent(eventName, properties);\n }\n\n identify(userId: string, traits?: BTSAnalyticsPayloadProperties): void {\n this.queue.push(\n this.buildEvent(\"identify\", \"identify\", {\n traits: traits ?? {},\n userId,\n }),\n );\n this.scheduleFlush();\n }\n\n listStandardEvents(): BTSAnalyticsStandardEventName[] {\n return listStandardWebEvents();\n }\n\n /** Start a cross-site funnel; persists journey token for decorateUrl. */\n async startFunnel(entryPath?: string): Promise<{ journeyId: string; journeyToken: string; expiresAt: number }> {\n const response = await this.postJson(\"/journey/start\", {\n siteKey: this.siteKey,\n entryPath: entryPath ?? currentPath(),\n });\n if (!response.ok) {\n throw new Error(`journey/start failed: ${response.status}`);\n }\n const data = (await response.json()) as {\n journeyId: string;\n journeyToken: string;\n expiresAt: number;\n };\n safeStorage()?.setItem(STORAGE_JOURNEY, data.journeyToken);\n return data;\n }\n\n getPersistedJourneyToken(): string | null {\n return safeStorage()?.getItem(STORAGE_JOURNEY) ?? null;\n }\n\n /** Append site key + journey token to a BTS URL (checkout, join, etc.). */\n decorateUrl(url: string, journeyToken?: string): string {\n const token = journeyToken ?? this.getPersistedJourneyToken();\n const next = new URL(url, currentOrigin());\n next.searchParams.set(QUERY_SITE, this.siteKey);\n if (token) {\n next.searchParams.set(QUERY_JOURNEY, token);\n }\n return next.toString();\n }\n\n /** Call from BTS after accepting the journey (optional; server can also infer). */\n async notifyHandoff(journeyToken: string, context?: Record<string, unknown>): Promise<void> {\n const response = await this.postJson(\"/journey/handoff\", { journeyToken, context });\n if (!response.ok) {\n throw new Error(`journey/handoff failed: ${response.status}`);\n }\n }\n\n destroy(): void {\n const win = safeWindow();\n const doc = safeDocument();\n if (doc && this.clickHandler && (this.autoTrack.outboundLinks || this.autoTrack.buttonClicks)) {\n doc.removeEventListener(\"click\", this.clickHandler, true);\n }\n if (doc && this.submitHandler && this.autoTrack.formSubmissions) {\n doc.removeEventListener(\"submit\", this.submitHandler, true);\n }\n if (win && this.popstateHandler) {\n win.removeEventListener(\"popstate\", this.popstateHandler);\n }\n if (win && this.pagehideHandler) {\n win.removeEventListener(\"pagehide\", this.pagehideHandler);\n }\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n this.unpatchHistory?.();\n this.unpatchHistory = null;\n this.flushWithBeacon();\n }\n}\n\nexport function createBTSAnalytics(init: BTSAnalyticsInit): BTSAnalytics {\n return BTSAnalytics.init(init);\n}\n\nexport { listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsStandardEventName };\n",
7
+ "import {\n BTSAnalytics,\n createBTSAnalytics,\n listStandardWebEvents,\n type BTSAnalyticsEventType,\n type BTSAnalyticsInit,\n type BTSAnalyticsStandardEventName,\n} from \"./index\";\n\ndeclare global {\n interface Window {\n BTSAnalytics?: {\n BTSAnalytics: typeof BTSAnalytics;\n createBTSAnalytics: typeof createBTSAnalytics;\n listStandardWebEvents: typeof listStandardWebEvents;\n };\n createBTSAnalytics?: typeof createBTSAnalytics;\n }\n}\n\nif (typeof window !== \"undefined\") {\n window.BTSAnalytics = {\n BTSAnalytics,\n createBTSAnalytics,\n listStandardWebEvents,\n };\n window.createBTSAnalytics = createBTSAnalytics;\n}\n\nexport {\n BTSAnalytics,\n createBTSAnalytics,\n listStandardWebEvents,\n type BTSAnalyticsEventType,\n type BTSAnalyticsInit,\n type BTSAnalyticsStandardEventName,\n};\n"
8
+ ],
9
+ "mappings": "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",
10
+ "debugId": "8CE5844CD6E1239864756E2164756E21",
11
+ "names": []
12
+ }
@@ -0,0 +1,4 @@
1
+ var{defineProperty:M,getOwnPropertyNames:U,getOwnPropertyDescriptor:l}=Object,I=Object.prototype.hasOwnProperty;var Q=new WeakMap,O=(E)=>{var _=Q.get(E),c;if(_)return _;if(_=M({},"__esModule",{value:!0}),E&&typeof E==="object"||typeof E==="function")U(E).map((N)=>!I.call(_,N)&&M(_,N,{get:()=>E[N],enumerable:!(c=l(E,N))||c.enumerable}));return Q.set(E,_),_};var w=(E,_)=>{for(var c in _)M(E,c,{get:_[c],enumerable:!0,configurable:!0,set:(N)=>_[c]=()=>N})};var a={};w(a,{listStandardWebEvents:()=>K,createBTSAnalytics:()=>o,BTSAnalytics:()=>L});module.exports=O(a);var j=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],y=new Set(j),g=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),v={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function R(E){return y.has(E)}function C(E){let _=E.trim();if(_.length===0)return{eventName:_,canonicalEventName:_,isStandard:!1};let c=_.toLowerCase(),N=v[c];if(N)return{eventName:N,canonicalEventName:N,aliasOf:N,isStandard:!0};return{eventName:_,canonicalEventName:_,isStandard:R(_)}}function k(E,_){if(_)return _;if(E==="page_view")return"page_view";if(E==="identify")return"identify";if(g.has(E))return"conversion";return"custom"}function K(){return[...j]}var Y="bts_analytics_vid",G="bts_analytics_sid",W="bts_analytics_jt",P="bts_analytics_attr",h="bts_site",x="bts_jt",b=300,T={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0},p=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"];function q(){return typeof window>"u"?null:window}function Z(){return typeof document>"u"?null:document}function D(){return q()?.localStorage??null}function f(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function m(E){return E!==null&&typeof E==="object"&&!Array.isArray(E)}function u(E){if(!E)return null;try{let _=JSON.parse(E);if(!m(_))return null;let c=m(_.utm)?Object.fromEntries(Object.entries(_.utm).filter((V)=>typeof V[1]==="string")):{},N=m(_.clickIds)?Object.fromEntries(Object.entries(_.clickIds).filter((V)=>typeof V[1]==="string")):{};return{utm:c,clickIds:N,landingUrl:typeof _.landingUrl==="string"?_.landingUrl:void 0,lastSeenAt:typeof _.lastSeenAt==="string"?_.lastSeenAt:new Date().toISOString(),referrer:typeof _.referrer==="string"?_.referrer:void 0}}catch{return null}}function S(){let E=D();return u(E?.getItem(P)??null)}function d(E){let _=D();if(!_)return;_.setItem(P,JSON.stringify(E))}function F(){let E=q();if(!E)return;return`${E.location.pathname}${E.location.search}`}function A(){return q()?.location.href}function H(){return q()?.location.origin??"https://behindthescenes.com"}function z(){return Z()?.referrer||void 0}function J(){let E=q();if(!E)return S();let _=new URLSearchParams(E.location.search),c={utm:{},clickIds:{},landingUrl:E.location.href,lastSeenAt:new Date().toISOString(),referrer:z()};for(let B of p){let X=_.get(B);if(!X)continue;if(B.startsWith("utm_")){c.utm[B]=X;continue}c.clickIds[B]=X}let N=S(),V={utm:{...N?.utm??{},...c.utm},clickIds:{...N?.clickIds??{},...c.clickIds},landingUrl:c.landingUrl??N?.landingUrl,lastSeenAt:c.lastSeenAt,referrer:c.referrer??N?.referrer};return d(V),V}function $(E){let _=E?.trim();return _?_.slice(0,512):void 0}function s(E){let _={...T};if(E.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1};if(E.autoTrack&&typeof E.autoTrack==="object")return{pageviews:E.autoTrack.pageviews??_.pageviews,history:E.autoTrack.history??_.history,outboundLinks:E.autoTrack.outboundLinks??_.outboundLinks,buttonClicks:E.autoTrack.buttonClicks??_.buttonClicks,formSubmissions:E.autoTrack.formSubmissions??_.formSubmissions};if(E.autoPageviews===!1)return{..._,pageviews:!1};if(E.autoPageviews===!0)return{..._,pageviews:!0};return _}class L{siteKey;endpoint;debug;flushIntervalMs;autoTrack;queue=[];flushTimer=null;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;constructor(E){if(this.siteKey=E.siteKey,this.endpoint=E.endpoint.replace(/\/$/,""),this.debug=E.debug??!1,this.flushIntervalMs=E.flushIntervalMs??b,this.autoTrack=s(E),J(),q())this.installAutoTracking()}static init(E){return new L(E)}getVisitorId(){let E=D(),_=E?.getItem(Y);if(_)return _;let c=f();return E?.setItem(Y,c),c}getSessionId(){let E=D(),_=E?.getItem(G);if(_)return _;let c=f();return E?.setItem(G,c),c}log(...E){if(this.debug)console.log("[@bts/analytics]",...E)}installAutoTracking(){let E=q(),_=Z();if(!E||!_)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(E),this.popstateHandler=()=>{this.recordPageView()},E.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(c)=>{this.handleAutoClick(c)},this.submitHandler=(c)=>{this.handleAutoSubmit(c)},this.pagehideHandler=()=>{this.flushWithBeacon()},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)_.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions)_.addEventListener("submit",this.submitHandler,!0);E.addEventListener("pagehide",this.pagehideHandler)}patchHistory(E){let _=E.history,c=_.pushState.bind(_),N=_.replaceState.bind(_),V=()=>{J(),this.recordPageView()};return _.pushState=(...B)=>{c(...B),V()},_.replaceState=(...B)=>{N(...B),V()},()=>{_.pushState=c,_.replaceState=N}}handleAutoClick(E){if(E.defaultPrevented)return;let _=E.target;if(!(_ instanceof Element))return;if(this.autoTrack.outboundLinks){let N=_.closest("a[href]");if(N instanceof HTMLAnchorElement){let V=$(N.href);if(!V)return;let B=new URL(V,H());if(B.origin!==H()){this.trackStandard("outbound_click",{elementHref:V,elementId:$(N.id),elementText:$(N.textContent),linkHost:B.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let c=_.closest("button,[role='button'],[data-bts-track-click]");if(!(c instanceof Element))return;this.trackStandard("button_click",{elementId:$(c.id),elementName:$(c.getAttribute("name")),elementRole:$(c.getAttribute("role")),elementText:$(c.textContent)})}handleAutoSubmit(E){if(E.defaultPrevented||!this.autoTrack.formSubmissions)return;let _=E.target;if(!(_ instanceof HTMLFormElement))return;this.trackStandard("form_submit",{formAction:$(_.action),formId:$(_.id),formMethod:$(_.method),formName:$(_.getAttribute("name"))})}buildEvent(E,_,c,N){let V=J(),B=N?.path??F(),X=z();return{eventName:E,eventType:_,path:B,referrer:X,occurredAt:new Date().toISOString(),visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...c??{},attribution:V?{utm:V.utm,clickIds:V.clickIds,landingUrl:V.landingUrl,referrer:V.referrer}:void 0,page:{title:Z()?.title,url:A()}}}}queueEvent(E,_,c){let N=C(E),V=k(N.canonicalEventName,c),B={..._??{}};if(N.aliasOf)B.originalEventName=E;if(this.queue.push(this.buildEvent(N.eventName,V,B)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(E,_){let c=`${this.endpoint}${E}`;return this.log("POST",c,_),fetch(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(_)})}flushWithBeacon(){if(this.queue.length===0)return;let E=q(),_=E?.navigator?.sendBeacon?.bind(E.navigator);if(!_)return;let c=[...this.queue];this.queue=[];let N=`${this.endpoint}/ingest/batch`,V=JSON.stringify({siteKey:this.siteKey,events:c}),B=new Blob([V],{type:"application/json"});_(N,B)}async flushNow(){if(this.queue.length===0)return;let E=[...this.queue];this.queue=[];try{let _=await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:E});if(!_.ok)this.log("flush failed",_.status)}catch(_){this.log("flush error",_)}}recordPageView(E){let _=E??F(),c=A()??_??"/";if(c===this.lastTrackedUrl)return;this.lastTrackedUrl=c,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:_})),this.scheduleFlush()}page(E){this.lastTrackedUrl=null,this.recordPageView(E)}track(E,_){this.queueEvent(E,_)}trackStandard(E,_){this.queueEvent(E,_)}identify(E,_){this.queue.push(this.buildEvent("identify","identify",{traits:_??{},userId:E})),this.scheduleFlush()}listStandardEvents(){return K()}async startFunnel(E){let _=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:E??F()});if(!_.ok)throw Error(`journey/start failed: ${_.status}`);let c=await _.json();return D()?.setItem(W,c.journeyToken),c}getPersistedJourneyToken(){return D()?.getItem(W)??null}decorateUrl(E,_){let c=_??this.getPersistedJourneyToken(),N=new URL(E,H());if(N.searchParams.set(h,this.siteKey),c)N.searchParams.set(x,c);return N.toString()}async notifyHandoff(E,_){let c=await this.postJson("/journey/handoff",{journeyToken:E,context:_});if(!c.ok)throw Error(`journey/handoff failed: ${c.status}`)}destroy(){let E=q(),_=Z();if(_&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))_.removeEventListener("click",this.clickHandler,!0);if(_&&this.submitHandler&&this.autoTrack.formSubmissions)_.removeEventListener("submit",this.submitHandler,!0);if(E&&this.popstateHandler)E.removeEventListener("popstate",this.popstateHandler);if(E&&this.pagehideHandler)E.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function o(E){return L.init(E)}
2
+
3
+ //# debugId=164F30813325DE1964756E2164756E21
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/events.ts", "../../src/index.ts"],
4
+ "sourcesContent": [
5
+ "export const STANDARD_WEB_EVENTS = [\n \"page_view\",\n \"view_content\",\n \"search\",\n \"lead\",\n \"sign_up\",\n \"begin_checkout\",\n \"add_payment_info\",\n \"purchase\",\n \"outbound_click\",\n \"button_click\",\n \"form_submit\",\n] as const;\n\nexport type BTSAnalyticsStandardEventName = (typeof STANDARD_WEB_EVENTS)[number];\n\nexport type BTSAnalyticsEventType = \"page_view\" | \"custom\" | \"identify\" | \"conversion\";\n\nconst STANDARD_EVENT_SET = new Set<string>(STANDARD_WEB_EVENTS);\n\nconst CONVERSION_EVENTS = new Set<string>([\"lead\", \"sign_up\", \"begin_checkout\", \"add_payment_info\", \"purchase\"]);\n\nconst LEGACY_EVENT_ALIASES: Record<string, BTSAnalyticsStandardEventName> = {\n $pageview: \"page_view\",\n checkout_started: \"begin_checkout\",\n lead_capture: \"lead\",\n purchase_completed: \"purchase\",\n registration_complete: \"sign_up\",\n};\n\nexport function isStandardWebEventName(eventName: string): eventName is BTSAnalyticsStandardEventName {\n return STANDARD_EVENT_SET.has(eventName);\n}\n\nexport function normalizeEventName(eventName: string): {\n eventName: string;\n canonicalEventName: string;\n aliasOf?: BTSAnalyticsStandardEventName;\n isStandard: boolean;\n} {\n const trimmed = eventName.trim();\n if (trimmed.length === 0) {\n return {\n eventName: trimmed,\n canonicalEventName: trimmed,\n isStandard: false,\n };\n }\n\n const lowered = trimmed.toLowerCase();\n const aliasOf = LEGACY_EVENT_ALIASES[lowered];\n if (aliasOf) {\n return {\n eventName: aliasOf,\n canonicalEventName: aliasOf,\n aliasOf,\n isStandard: true,\n };\n }\n\n return {\n eventName: trimmed,\n canonicalEventName: trimmed,\n isStandard: isStandardWebEventName(trimmed),\n };\n}\n\nexport function resolveEventType(eventName: string, explicitType?: BTSAnalyticsEventType): BTSAnalyticsEventType {\n if (explicitType) {\n return explicitType;\n }\n if (eventName === \"page_view\") {\n return \"page_view\";\n }\n if (eventName === \"identify\") {\n return \"identify\";\n }\n if (CONVERSION_EVENTS.has(eventName)) {\n return \"conversion\";\n }\n return \"custom\";\n}\n\nexport function listStandardWebEvents(): BTSAnalyticsStandardEventName[] {\n return [...STANDARD_WEB_EVENTS];\n}\n",
6
+ "import {\n listStandardWebEvents,\n normalizeEventName,\n resolveEventType,\n type BTSAnalyticsEventType,\n type BTSAnalyticsStandardEventName,\n} from \"./events\";\n\nconst STORAGE_VISITOR = \"bts_analytics_vid\";\nconst STORAGE_SESSION = \"bts_analytics_sid\";\nconst STORAGE_JOURNEY = \"bts_analytics_jt\";\nconst STORAGE_ATTRIBUTION = \"bts_analytics_attr\";\nconst QUERY_SITE = \"bts_site\";\nconst QUERY_JOURNEY = \"bts_jt\";\n\nconst DEFAULT_FLUSH_INTERVAL_MS = 300;\n\nconst DEFAULT_AUTO_TRACK = {\n pageviews: true,\n history: true,\n outboundLinks: true,\n buttonClicks: true,\n formSubmissions: true,\n} as const;\n\nconst ATTRIBUTION_QUERY_KEYS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_term\",\n \"utm_content\",\n \"fbclid\",\n \"gclid\",\n \"gbraid\",\n \"li_fat_id\",\n \"msclkid\",\n \"ttclid\",\n \"wbraid\",\n] as const;\n\ntype AutoTrackConfig = {\n pageviews: boolean;\n history: boolean;\n outboundLinks: boolean;\n buttonClicks: boolean;\n formSubmissions: boolean;\n};\n\nexport type BTSAnalyticsInit = {\n siteKey: string;\n endpoint: string;\n autoPageviews?: boolean;\n autoTrack?: boolean | Partial<AutoTrackConfig>;\n debug?: boolean;\n flushIntervalMs?: number;\n};\n\nexport type BTSAnalyticsPayloadProperties = Record<string, unknown>;\n\ntype StoredAttribution = {\n utm: Record<string, string>;\n clickIds: Record<string, string>;\n landingUrl?: string;\n lastSeenAt: string;\n referrer?: string;\n};\n\ntype QueuedAnalyticsEvent = {\n eventName: string;\n eventType: BTSAnalyticsEventType;\n path?: string;\n referrer?: string;\n occurredAt: string;\n visitorId: string;\n sessionId: string;\n properties: BTSAnalyticsPayloadProperties;\n};\n\nfunction safeWindow(): Window | null {\n return typeof window === \"undefined\" ? null : window;\n}\n\nfunction safeDocument(): Document | null {\n return typeof document === \"undefined\" ? null : document;\n}\n\nfunction safeStorage(): Storage | null {\n const win = safeWindow();\n return win?.localStorage ?? null;\n}\n\nfunction randomId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n return `v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction parseStoredAttribution(raw: string | null): StoredAttribution | null {\n if (!raw) {\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!isRecord(parsed)) {\n return null;\n }\n const utm = isRecord(parsed.utm)\n ? Object.fromEntries(Object.entries(parsed.utm).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"))\n : {};\n const clickIds = isRecord(parsed.clickIds)\n ? Object.fromEntries(\n Object.entries(parsed.clickIds).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"),\n )\n : {};\n return {\n utm,\n clickIds,\n landingUrl: typeof parsed.landingUrl === \"string\" ? parsed.landingUrl : undefined,\n lastSeenAt: typeof parsed.lastSeenAt === \"string\" ? parsed.lastSeenAt : new Date().toISOString(),\n referrer: typeof parsed.referrer === \"string\" ? parsed.referrer : undefined,\n };\n } catch {\n return null;\n }\n}\n\nfunction readStoredAttribution(): StoredAttribution | null {\n const storage = safeStorage();\n return parseStoredAttribution(storage?.getItem(STORAGE_ATTRIBUTION) ?? null);\n}\n\nfunction writeStoredAttribution(next: StoredAttribution): void {\n const storage = safeStorage();\n if (!storage) {\n return;\n }\n storage.setItem(STORAGE_ATTRIBUTION, JSON.stringify(next));\n}\n\nfunction currentPath(): string | undefined {\n const win = safeWindow();\n if (!win) {\n return undefined;\n }\n return `${win.location.pathname}${win.location.search}`;\n}\n\nfunction currentUrl(): string | undefined {\n return safeWindow()?.location.href;\n}\n\nfunction currentOrigin(): string {\n return safeWindow()?.location.origin ?? \"https://behindthescenes.com\";\n}\n\nfunction currentReferrer(): string | undefined {\n return safeDocument()?.referrer || undefined;\n}\n\nfunction readCurrentAttribution(): StoredAttribution | null {\n const win = safeWindow();\n if (!win) {\n return readStoredAttribution();\n }\n const params = new URLSearchParams(win.location.search);\n const next: StoredAttribution = {\n utm: {},\n clickIds: {},\n landingUrl: win.location.href,\n lastSeenAt: new Date().toISOString(),\n referrer: currentReferrer(),\n };\n for (const key of ATTRIBUTION_QUERY_KEYS) {\n const value = params.get(key);\n if (!value) {\n continue;\n }\n if (key.startsWith(\"utm_\")) {\n next.utm[key] = value;\n continue;\n }\n next.clickIds[key] = value;\n }\n\n const previous = readStoredAttribution();\n const merged: StoredAttribution = {\n utm: { ...(previous?.utm ?? {}), ...next.utm },\n clickIds: { ...(previous?.clickIds ?? {}), ...next.clickIds },\n landingUrl: next.landingUrl ?? previous?.landingUrl,\n lastSeenAt: next.lastSeenAt,\n referrer: next.referrer ?? previous?.referrer,\n };\n writeStoredAttribution(merged);\n return merged;\n}\n\nfunction sanitizeText(value: string | null | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed.slice(0, 512) : undefined;\n}\n\nfunction createAutoTrackConfig(init: BTSAnalyticsInit): AutoTrackConfig {\n const base = { ...DEFAULT_AUTO_TRACK };\n if (init.autoTrack === false) {\n return {\n pageviews: false,\n history: false,\n outboundLinks: false,\n buttonClicks: false,\n formSubmissions: false,\n };\n }\n if (init.autoTrack && typeof init.autoTrack === \"object\") {\n return {\n pageviews: init.autoTrack.pageviews ?? base.pageviews,\n history: init.autoTrack.history ?? base.history,\n outboundLinks: init.autoTrack.outboundLinks ?? base.outboundLinks,\n buttonClicks: init.autoTrack.buttonClicks ?? base.buttonClicks,\n formSubmissions: init.autoTrack.formSubmissions ?? base.formSubmissions,\n };\n }\n if (init.autoPageviews === false) {\n return {\n ...base,\n pageviews: false,\n }\n }\n if (init.autoPageviews === true) {\n return {\n ...base,\n pageviews: true,\n }\n }\n return base;\n}\n\nexport class BTSAnalytics {\n private siteKey: string;\n private endpoint: string;\n private debug: boolean;\n private flushIntervalMs: number;\n private autoTrack: AutoTrackConfig;\n private queue: QueuedAnalyticsEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private lastTrackedUrl: string | null = null;\n private unpatchHistory: (() => void) | null = null;\n private clickHandler: ((event: Event) => void) | null = null;\n private submitHandler: ((event: Event) => void) | null = null;\n private pagehideHandler: (() => void) | null = null;\n private popstateHandler: (() => void) | null = null;\n\n constructor(init: BTSAnalyticsInit) {\n this.siteKey = init.siteKey;\n this.endpoint = init.endpoint.replace(/\\/$/, \"\");\n this.debug = init.debug ?? false;\n this.flushIntervalMs = init.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.autoTrack = createAutoTrackConfig(init);\n\n readCurrentAttribution();\n\n if (safeWindow()) {\n this.installAutoTracking();\n }\n }\n\n static init(opts: BTSAnalyticsInit): BTSAnalytics {\n return new BTSAnalytics(opts);\n }\n\n getVisitorId(): string {\n const storage = safeStorage();\n const existing = storage?.getItem(STORAGE_VISITOR);\n if (existing) {\n return existing;\n }\n const id = randomId();\n storage?.setItem(STORAGE_VISITOR, id);\n return id;\n }\n\n getSessionId(): string {\n const storage = safeStorage();\n const existing = storage?.getItem(STORAGE_SESSION);\n if (existing) {\n return existing;\n }\n const id = randomId();\n storage?.setItem(STORAGE_SESSION, id);\n return id;\n }\n\n private log(...args: unknown[]) {\n if (this.debug) {\n // eslint-disable-next-line no-console\n console.log(\"[@bts/analytics]\", ...args);\n }\n }\n\n private installAutoTracking(): void {\n const win = safeWindow();\n const doc = safeDocument();\n if (!win || !doc) {\n return;\n }\n\n if (this.autoTrack.pageviews) {\n this.recordPageView();\n }\n\n if (this.autoTrack.history) {\n this.unpatchHistory = this.patchHistory(win);\n this.popstateHandler = () => {\n this.recordPageView();\n };\n win.addEventListener(\"popstate\", this.popstateHandler);\n }\n\n this.clickHandler = (event: Event) => {\n this.handleAutoClick(event);\n };\n this.submitHandler = (event: Event) => {\n this.handleAutoSubmit(event);\n };\n this.pagehideHandler = () => {\n this.flushWithBeacon();\n };\n\n if (this.autoTrack.outboundLinks || this.autoTrack.buttonClicks) {\n doc.addEventListener(\"click\", this.clickHandler, true);\n }\n if (this.autoTrack.formSubmissions) {\n doc.addEventListener(\"submit\", this.submitHandler, true);\n }\n win.addEventListener(\"pagehide\", this.pagehideHandler);\n }\n\n private patchHistory(win: Window): () => void {\n const history = win.history;\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n const onHistoryChange = () => {\n readCurrentAttribution();\n this.recordPageView();\n };\n\n history.pushState = ((...args: Parameters<History[\"pushState\"]>) => {\n originalPushState(...args);\n onHistoryChange();\n }) as History[\"pushState\"];\n\n history.replaceState = ((...args: Parameters<History[\"replaceState\"]>) => {\n originalReplaceState(...args);\n onHistoryChange();\n }) as History[\"replaceState\"];\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n };\n }\n\n private handleAutoClick(event: Event): void {\n if (event.defaultPrevented) {\n return;\n }\n const target = event.target;\n if (!(target instanceof Element)) {\n return;\n }\n\n if (this.autoTrack.outboundLinks) {\n const anchor = target.closest(\"a[href]\");\n if (anchor instanceof HTMLAnchorElement) {\n const href = sanitizeText(anchor.href);\n if (!href) {\n return;\n }\n const destination = new URL(href, currentOrigin());\n if (destination.origin !== currentOrigin()) {\n this.trackStandard(\"outbound_click\", {\n elementHref: href,\n elementId: sanitizeText(anchor.id),\n elementText: sanitizeText(anchor.textContent),\n linkHost: destination.hostname,\n });\n return;\n }\n }\n }\n\n if (!this.autoTrack.buttonClicks) {\n return;\n }\n\n const button = target.closest(\"button,[role='button'],[data-bts-track-click]\");\n if (!(button instanceof Element)) {\n return;\n }\n this.trackStandard(\"button_click\", {\n elementId: sanitizeText(button.id),\n elementName: sanitizeText(button.getAttribute(\"name\")),\n elementRole: sanitizeText(button.getAttribute(\"role\")),\n elementText: sanitizeText(button.textContent),\n });\n }\n\n private handleAutoSubmit(event: Event): void {\n if (event.defaultPrevented || !this.autoTrack.formSubmissions) {\n return;\n }\n const target = event.target;\n if (!(target instanceof HTMLFormElement)) {\n return;\n }\n this.trackStandard(\"form_submit\", {\n formAction: sanitizeText(target.action),\n formId: sanitizeText(target.id),\n formMethod: sanitizeText(target.method),\n formName: sanitizeText(target.getAttribute(\"name\")),\n });\n }\n\n private buildEvent(\n eventName: string,\n eventType: BTSAnalyticsEventType,\n properties?: BTSAnalyticsPayloadProperties,\n overrides?: { path?: string },\n ): QueuedAnalyticsEvent {\n const attribution = readCurrentAttribution();\n const path = overrides?.path ?? currentPath();\n const referrer = currentReferrer();\n return {\n eventName,\n eventType,\n path,\n referrer,\n occurredAt: new Date().toISOString(),\n visitorId: this.getVisitorId(),\n sessionId: this.getSessionId(),\n properties: {\n ...(properties ?? {}),\n attribution: attribution\n ? {\n utm: attribution.utm,\n clickIds: attribution.clickIds,\n landingUrl: attribution.landingUrl,\n referrer: attribution.referrer,\n }\n : undefined,\n page: {\n title: safeDocument()?.title,\n url: currentUrl(),\n },\n },\n };\n }\n\n private queueEvent(eventName: string, properties?: BTSAnalyticsPayloadProperties, explicitType?: BTSAnalyticsEventType): void {\n const normalized = normalizeEventName(eventName);\n const eventType = resolveEventType(normalized.canonicalEventName, explicitType);\n const nextProperties: BTSAnalyticsPayloadProperties = {\n ...(properties ?? {}),\n };\n if (normalized.aliasOf) {\n nextProperties.originalEventName = eventName;\n }\n this.queue.push(this.buildEvent(normalized.eventName, eventType, nextProperties));\n if (this.queue.length >= 50) {\n void this.flushNow();\n return;\n }\n this.scheduleFlush();\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer) {\n return;\n }\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n void this.flushNow();\n }, this.flushIntervalMs);\n }\n\n private async postJson(path: string, body: unknown): Promise<Response> {\n const url = `${this.endpoint}${path}`;\n this.log(\"POST\", url, body);\n return fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n }\n\n private flushWithBeacon(): void {\n if (this.queue.length === 0) {\n return;\n }\n const win = safeWindow();\n const beacon = win?.navigator?.sendBeacon?.bind(win.navigator);\n if (!beacon) {\n return;\n }\n const batch = [...this.queue];\n this.queue = [];\n const url = `${this.endpoint}/ingest/batch`;\n const body = JSON.stringify({ siteKey: this.siteKey, events: batch });\n const blob = new Blob([body], { type: \"application/json\" });\n beacon(url, blob);\n }\n\n async flushNow(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n const batch = [...this.queue];\n this.queue = [];\n try {\n const response = await this.postJson(\"/ingest/batch\", {\n siteKey: this.siteKey,\n events: batch,\n });\n if (!response.ok) {\n this.log(\"flush failed\", response.status);\n }\n } catch (error) {\n this.log(\"flush error\", error);\n }\n }\n\n private recordPageView(path?: string): void {\n const derivedPath = path ?? currentPath();\n const url = currentUrl() ?? derivedPath ?? \"/\";\n if (url === this.lastTrackedUrl) {\n return;\n }\n this.lastTrackedUrl = url;\n this.queue.push(\n this.buildEvent(\n \"page_view\",\n \"page_view\",\n {\n autoCaptured: true,\n },\n { path: derivedPath },\n ),\n );\n this.scheduleFlush();\n }\n\n page(path?: string): void {\n this.lastTrackedUrl = null;\n this.recordPageView(path);\n }\n\n track(eventName: string, properties?: BTSAnalyticsPayloadProperties): void {\n this.queueEvent(eventName, properties);\n }\n\n trackStandard(eventName: BTSAnalyticsStandardEventName, properties?: BTSAnalyticsPayloadProperties): void {\n this.queueEvent(eventName, properties);\n }\n\n identify(userId: string, traits?: BTSAnalyticsPayloadProperties): void {\n this.queue.push(\n this.buildEvent(\"identify\", \"identify\", {\n traits: traits ?? {},\n userId,\n }),\n );\n this.scheduleFlush();\n }\n\n listStandardEvents(): BTSAnalyticsStandardEventName[] {\n return listStandardWebEvents();\n }\n\n /** Start a cross-site funnel; persists journey token for decorateUrl. */\n async startFunnel(entryPath?: string): Promise<{ journeyId: string; journeyToken: string; expiresAt: number }> {\n const response = await this.postJson(\"/journey/start\", {\n siteKey: this.siteKey,\n entryPath: entryPath ?? currentPath(),\n });\n if (!response.ok) {\n throw new Error(`journey/start failed: ${response.status}`);\n }\n const data = (await response.json()) as {\n journeyId: string;\n journeyToken: string;\n expiresAt: number;\n };\n safeStorage()?.setItem(STORAGE_JOURNEY, data.journeyToken);\n return data;\n }\n\n getPersistedJourneyToken(): string | null {\n return safeStorage()?.getItem(STORAGE_JOURNEY) ?? null;\n }\n\n /** Append site key + journey token to a BTS URL (checkout, join, etc.). */\n decorateUrl(url: string, journeyToken?: string): string {\n const token = journeyToken ?? this.getPersistedJourneyToken();\n const next = new URL(url, currentOrigin());\n next.searchParams.set(QUERY_SITE, this.siteKey);\n if (token) {\n next.searchParams.set(QUERY_JOURNEY, token);\n }\n return next.toString();\n }\n\n /** Call from BTS after accepting the journey (optional; server can also infer). */\n async notifyHandoff(journeyToken: string, context?: Record<string, unknown>): Promise<void> {\n const response = await this.postJson(\"/journey/handoff\", { journeyToken, context });\n if (!response.ok) {\n throw new Error(`journey/handoff failed: ${response.status}`);\n }\n }\n\n destroy(): void {\n const win = safeWindow();\n const doc = safeDocument();\n if (doc && this.clickHandler && (this.autoTrack.outboundLinks || this.autoTrack.buttonClicks)) {\n doc.removeEventListener(\"click\", this.clickHandler, true);\n }\n if (doc && this.submitHandler && this.autoTrack.formSubmissions) {\n doc.removeEventListener(\"submit\", this.submitHandler, true);\n }\n if (win && this.popstateHandler) {\n win.removeEventListener(\"popstate\", this.popstateHandler);\n }\n if (win && this.pagehideHandler) {\n win.removeEventListener(\"pagehide\", this.pagehideHandler);\n }\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n this.unpatchHistory?.();\n this.unpatchHistory = null;\n this.flushWithBeacon();\n }\n}\n\nexport function createBTSAnalytics(init: BTSAnalyticsInit): BTSAnalytics {\n return BTSAnalytics.init(init);\n}\n\nexport { listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsStandardEventName };\n"
7
+ ],
8
+ "mappings": "qjBAAO,IAAM,EAAsB,CACjC,YACA,eACA,SACA,OACA,UACA,iBACA,mBACA,WACA,iBACA,eACA,aACF,EAMM,EAAqB,IAAI,IAAY,CAAmB,EAExD,EAAoB,IAAI,IAAY,CAAC,OAAQ,UAAW,iBAAkB,mBAAoB,UAAU,CAAC,EAEzG,EAAsE,CAC1E,UAAW,YACX,iBAAkB,iBAClB,aAAc,OACd,mBAAoB,WACpB,sBAAuB,SACzB,EAEO,SAAS,CAAsB,CAAC,EAA+D,CACpG,OAAO,EAAmB,IAAI,CAAS,EAGlC,SAAS,CAAkB,CAAC,EAKjC,CACA,IAAM,EAAU,EAAU,KAAK,EAC/B,GAAI,EAAQ,SAAW,EACrB,MAAO,CACL,UAAW,EACX,mBAAoB,EACpB,WAAY,EACd,EAGF,IAAM,EAAU,EAAQ,YAAY,EAC9B,EAAU,EAAqB,GACrC,GAAI,EACF,MAAO,CACL,UAAW,EACX,mBAAoB,EACpB,UACA,WAAY,EACd,EAGF,MAAO,CACL,UAAW,EACX,mBAAoB,EACpB,WAAY,EAAuB,CAAO,CAC5C,EAGK,SAAS,CAAgB,CAAC,EAAmB,EAA6D,CAC/G,GAAI,EACF,OAAO,EAET,GAAI,IAAc,YAChB,MAAO,YAET,GAAI,IAAc,WAChB,MAAO,WAET,GAAI,EAAkB,IAAI,CAAS,EACjC,MAAO,aAET,MAAO,SAGF,SAAS,CAAqB,EAAoC,CACvE,MAAO,CAAC,GAAG,CAAmB,EC5EhC,IAAM,EAAkB,oBAClB,EAAkB,oBAClB,EAAkB,mBAClB,EAAsB,qBACtB,EAAa,WACb,EAAgB,SAEhB,EAA4B,IAE5B,EAAqB,CACzB,UAAW,GACX,QAAS,GACT,cAAe,GACf,aAAc,GACd,gBAAiB,EACnB,EAEM,EAAyB,CAC7B,aACA,aACA,eACA,WACA,cACA,SACA,QACA,SACA,YACA,UACA,SACA,QACF,EAwCA,SAAS,CAAU,EAAkB,CACnC,OAAO,OAAO,OAAW,IAAc,KAAO,OAGhD,SAAS,CAAY,EAAoB,CACvC,OAAO,OAAO,SAAa,IAAc,KAAO,SAGlD,SAAS,CAAW,EAAmB,CAErC,OADY,EAAW,GACX,cAAgB,KAG9B,SAAS,CAAQ,EAAW,CAC1B,GAAI,OAAO,OAAW,KAAe,OAAO,OAAO,aAAe,WAChE,OAAO,OAAO,WAAW,EAE3B,MAAO,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE,IAG1E,SAAS,CAAQ,CAAC,EAAkD,CAClE,OAAO,IAAU,MAAQ,OAAO,IAAU,UAAY,CAAC,MAAM,QAAQ,CAAK,EAG5E,SAAS,CAAsB,CAAC,EAA8C,CAC5E,GAAI,CAAC,EACH,OAAO,KAET,GAAI,CACF,IAAM,EAAS,KAAK,MAAM,CAAG,EAC7B,GAAI,CAAC,EAAS,CAAM,EAClB,OAAO,KAET,IAAM,EAAM,EAAS,EAAO,GAAG,EAC3B,OAAO,YAAY,OAAO,QAAQ,EAAO,GAAG,EAAE,OAAO,CAAC,IAAqC,OAAO,EAAM,KAAO,QAAQ,CAAC,EACxH,CAAC,EACC,EAAW,EAAS,EAAO,QAAQ,EACrC,OAAO,YACP,OAAO,QAAQ,EAAO,QAAQ,EAAE,OAAO,CAAC,IAAqC,OAAO,EAAM,KAAO,QAAQ,CAC3G,EACE,CAAC,EACL,MAAO,CACL,MACA,WACA,WAAY,OAAO,EAAO,aAAe,SAAW,EAAO,WAAa,OACxE,WAAY,OAAO,EAAO,aAAe,SAAW,EAAO,WAAa,IAAI,KAAK,EAAE,YAAY,EAC/F,SAAU,OAAO,EAAO,WAAa,SAAW,EAAO,SAAW,MACpE,EACA,KAAM,CACN,OAAO,MAIX,SAAS,CAAqB,EAA6B,CACzD,IAAM,EAAU,EAAY,EAC5B,OAAO,EAAuB,GAAS,QAAQ,CAAmB,GAAK,IAAI,EAG7E,SAAS,CAAsB,CAAC,EAA+B,CAC7D,IAAM,EAAU,EAAY,EAC5B,GAAI,CAAC,EACH,OAEF,EAAQ,QAAQ,EAAqB,KAAK,UAAU,CAAI,CAAC,EAG3D,SAAS,CAAW,EAAuB,CACzC,IAAM,EAAM,EAAW,EACvB,GAAI,CAAC,EACH,OAEF,MAAO,GAAG,EAAI,SAAS,WAAW,EAAI,SAAS,SAGjD,SAAS,CAAU,EAAuB,CACxC,OAAO,EAAW,GAAG,SAAS,KAGhC,SAAS,CAAa,EAAW,CAC/B,OAAO,EAAW,GAAG,SAAS,QAAU,8BAG1C,SAAS,CAAe,EAAuB,CAC7C,OAAO,EAAa,GAAG,UAAY,OAGrC,SAAS,CAAsB,EAA6B,CAC1D,IAAM,EAAM,EAAW,EACvB,GAAI,CAAC,EACH,OAAO,EAAsB,EAE/B,IAAM,EAAS,IAAI,gBAAgB,EAAI,SAAS,MAAM,EAChD,EAA0B,CAC9B,IAAK,CAAC,EACN,SAAU,CAAC,EACX,WAAY,EAAI,SAAS,KACzB,WAAY,IAAI,KAAK,EAAE,YAAY,EACnC,SAAU,EAAgB,CAC5B,EACA,QAAW,KAAO,EAAwB,CACxC,IAAM,EAAQ,EAAO,IAAI,CAAG,EAC5B,GAAI,CAAC,EACH,SAEF,GAAI,EAAI,WAAW,MAAM,EAAG,CAC1B,EAAK,IAAI,GAAO,EAChB,SAEF,EAAK,SAAS,GAAO,EAGvB,IAAM,EAAW,EAAsB,EACjC,EAA4B,CAChC,IAAK,IAAM,GAAU,KAAO,CAAC,KAAO,EAAK,GAAI,EAC7C,SAAU,IAAM,GAAU,UAAY,CAAC,KAAO,EAAK,QAAS,EAC5D,WAAY,EAAK,YAAc,GAAU,WACzC,WAAY,EAAK,WACjB,SAAU,EAAK,UAAY,GAAU,QACvC,EAEA,OADA,EAAuB,CAAM,EACtB,EAGT,SAAS,CAAY,CAAC,EAAsD,CAC1E,IAAM,EAAU,GAAO,KAAK,EAC5B,OAAO,EAAU,EAAQ,MAAM,EAAG,GAAG,EAAI,OAG3C,SAAS,CAAqB,CAAC,EAAyC,CACtE,IAAM,EAAO,IAAK,CAAmB,EACrC,GAAI,EAAK,YAAc,GACrB,MAAO,CACL,UAAW,GACX,QAAS,GACT,cAAe,GACf,aAAc,GACd,gBAAiB,EACnB,EAEF,GAAI,EAAK,WAAa,OAAO,EAAK,YAAc,SAC9C,MAAO,CACL,UAAW,EAAK,UAAU,WAAa,EAAK,UAC5C,QAAS,EAAK,UAAU,SAAW,EAAK,QACxC,cAAe,EAAK,UAAU,eAAiB,EAAK,cACpD,aAAc,EAAK,UAAU,cAAgB,EAAK,aAClD,gBAAiB,EAAK,UAAU,iBAAmB,EAAK,eAC1D,EAEF,GAAI,EAAK,gBAAkB,GACzB,MAAO,IACF,EACH,UAAW,EACb,EAEF,GAAI,EAAK,gBAAkB,GACzB,MAAO,IACF,EACH,UAAW,EACb,EAEF,OAAO,EAGF,MAAM,CAAa,CAChB,QACA,SACA,MACA,gBACA,UACA,MAAgC,CAAC,EACjC,WAAmD,KACnD,eAAgC,KAChC,eAAsC,KACtC,aAAgD,KAChD,cAAiD,KACjD,gBAAuC,KACvC,gBAAuC,KAE/C,WAAW,CAAC,EAAwB,CASlC,GARA,KAAK,QAAU,EAAK,QACpB,KAAK,SAAW,EAAK,SAAS,QAAQ,MAAO,EAAE,EAC/C,KAAK,MAAQ,EAAK,OAAS,GAC3B,KAAK,gBAAkB,EAAK,iBAAmB,EAC/C,KAAK,UAAY,EAAsB,CAAI,EAE3C,EAAuB,EAEnB,EAAW,EACb,KAAK,oBAAoB,QAItB,KAAI,CAAC,EAAsC,CAChD,OAAO,IAAI,EAAa,CAAI,EAG9B,YAAY,EAAW,CACrB,IAAM,EAAU,EAAY,EACtB,EAAW,GAAS,QAAQ,CAAe,EACjD,GAAI,EACF,OAAO,EAET,IAAM,EAAK,EAAS,EAEpB,OADA,GAAS,QAAQ,EAAiB,CAAE,EAC7B,EAGT,YAAY,EAAW,CACrB,IAAM,EAAU,EAAY,EACtB,EAAW,GAAS,QAAQ,CAAe,EACjD,GAAI,EACF,OAAO,EAET,IAAM,EAAK,EAAS,EAEpB,OADA,GAAS,QAAQ,EAAiB,CAAE,EAC7B,EAGD,GAAG,IAAI,EAAiB,CAC9B,GAAI,KAAK,MAEP,QAAQ,IAAI,mBAAoB,GAAG,CAAI,EAInC,mBAAmB,EAAS,CAClC,IAAM,EAAM,EAAW,EACjB,EAAM,EAAa,EACzB,GAAI,CAAC,GAAO,CAAC,EACX,OAGF,GAAI,KAAK,UAAU,UACjB,KAAK,eAAe,EAGtB,GAAI,KAAK,UAAU,QACjB,KAAK,eAAiB,KAAK,aAAa,CAAG,EAC3C,KAAK,gBAAkB,IAAM,CAC3B,KAAK,eAAe,GAEtB,EAAI,iBAAiB,WAAY,KAAK,eAAe,EAavD,GAVA,KAAK,aAAe,CAAC,IAAiB,CACpC,KAAK,gBAAgB,CAAK,GAE5B,KAAK,cAAgB,CAAC,IAAiB,CACrC,KAAK,iBAAiB,CAAK,GAE7B,KAAK,gBAAkB,IAAM,CAC3B,KAAK,gBAAgB,GAGnB,KAAK,UAAU,eAAiB,KAAK,UAAU,aACjD,EAAI,iBAAiB,QAAS,KAAK,aAAc,EAAI,EAEvD,GAAI,KAAK,UAAU,gBACjB,EAAI,iBAAiB,SAAU,KAAK,cAAe,EAAI,EAEzD,EAAI,iBAAiB,WAAY,KAAK,eAAe,EAG/C,YAAY,CAAC,EAAyB,CAC5C,IAAM,EAAU,EAAI,QACd,EAAoB,EAAQ,UAAU,KAAK,CAAO,EAClD,EAAuB,EAAQ,aAAa,KAAK,CAAO,EAExD,EAAkB,IAAM,CAC5B,EAAuB,EACvB,KAAK,eAAe,GAatB,OAVA,EAAQ,UAAa,IAAI,IAA2C,CAClE,EAAkB,GAAG,CAAI,EACzB,EAAgB,GAGlB,EAAQ,aAAgB,IAAI,IAA8C,CACxE,EAAqB,GAAG,CAAI,EAC5B,EAAgB,GAGX,IAAM,CACX,EAAQ,UAAY,EACpB,EAAQ,aAAe,GAInB,eAAe,CAAC,EAAoB,CAC1C,GAAI,EAAM,iBACR,OAEF,IAAM,EAAS,EAAM,OACrB,GAAI,EAAE,aAAkB,SACtB,OAGF,GAAI,KAAK,UAAU,cAAe,CAChC,IAAM,EAAS,EAAO,QAAQ,SAAS,EACvC,GAAI,aAAkB,kBAAmB,CACvC,IAAM,EAAO,EAAa,EAAO,IAAI,EACrC,GAAI,CAAC,EACH,OAEF,IAAM,EAAc,IAAI,IAAI,EAAM,EAAc,CAAC,EACjD,GAAI,EAAY,SAAW,EAAc,EAAG,CAC1C,KAAK,cAAc,iBAAkB,CACnC,YAAa,EACb,UAAW,EAAa,EAAO,EAAE,EACjC,YAAa,EAAa,EAAO,WAAW,EAC5C,SAAU,EAAY,QACxB,CAAC,EACD,SAKN,GAAI,CAAC,KAAK,UAAU,aAClB,OAGF,IAAM,EAAS,EAAO,QAAQ,+CAA+C,EAC7E,GAAI,EAAE,aAAkB,SACtB,OAEF,KAAK,cAAc,eAAgB,CACjC,UAAW,EAAa,EAAO,EAAE,EACjC,YAAa,EAAa,EAAO,aAAa,MAAM,CAAC,EACrD,YAAa,EAAa,EAAO,aAAa,MAAM,CAAC,EACrD,YAAa,EAAa,EAAO,WAAW,CAC9C,CAAC,EAGK,gBAAgB,CAAC,EAAoB,CAC3C,GAAI,EAAM,kBAAoB,CAAC,KAAK,UAAU,gBAC5C,OAEF,IAAM,EAAS,EAAM,OACrB,GAAI,EAAE,aAAkB,iBACtB,OAEF,KAAK,cAAc,cAAe,CAChC,WAAY,EAAa,EAAO,MAAM,EACtC,OAAQ,EAAa,EAAO,EAAE,EAC9B,WAAY,EAAa,EAAO,MAAM,EACtC,SAAU,EAAa,EAAO,aAAa,MAAM,CAAC,CACpD,CAAC,EAGK,UAAU,CAChB,EACA,EACA,EACA,EACsB,CACtB,IAAM,EAAc,EAAuB,EACrC,EAAO,GAAW,MAAQ,EAAY,EACtC,EAAW,EAAgB,EACjC,MAAO,CACL,YACA,YACA,OACA,WACA,WAAY,IAAI,KAAK,EAAE,YAAY,EACnC,UAAW,KAAK,aAAa,EAC7B,UAAW,KAAK,aAAa,EAC7B,WAAY,IACN,GAAc,CAAC,EACnB,YAAa,EACT,CACA,IAAK,EAAY,IACjB,SAAU,EAAY,SACtB,WAAY,EAAY,WACxB,SAAU,EAAY,QACxB,EACE,OACJ,KAAM,CACJ,MAAO,EAAa,GAAG,MACvB,IAAK,EAAW,CAClB,CACF,CACF,EAGM,UAAU,CAAC,EAAmB,EAA4C,EAA4C,CAC5H,IAAM,EAAa,EAAmB,CAAS,EACzC,EAAY,EAAiB,EAAW,mBAAoB,CAAY,EACxE,EAAgD,IAChD,GAAc,CAAC,CACrB,EACA,GAAI,EAAW,QACb,EAAe,kBAAoB,EAGrC,GADA,KAAK,MAAM,KAAK,KAAK,WAAW,EAAW,UAAW,EAAW,CAAc,CAAC,EAC5E,KAAK,MAAM,QAAU,GAAI,CACtB,KAAK,SAAS,EACnB,OAEF,KAAK,cAAc,EAGb,aAAa,EAAS,CAC5B,GAAI,KAAK,WACP,OAEF,KAAK,WAAa,WAAW,IAAM,CACjC,KAAK,WAAa,KACb,KAAK,SAAS,GAClB,KAAK,eAAe,OAGX,SAAQ,CAAC,EAAc,EAAkC,CACrE,IAAM,EAAM,GAAG,KAAK,WAAW,IAE/B,OADA,KAAK,IAAI,OAAQ,EAAK,CAAI,EACnB,MAAM,EAAK,CAChB,OAAQ,OACR,QAAS,CAAE,eAAgB,kBAAmB,EAC9C,KAAM,KAAK,UAAU,CAAI,CAC3B,CAAC,EAGK,eAAe,EAAS,CAC9B,GAAI,KAAK,MAAM,SAAW,EACxB,OAEF,IAAM,EAAM,EAAW,EACjB,EAAS,GAAK,WAAW,YAAY,KAAK,EAAI,SAAS,EAC7D,GAAI,CAAC,EACH,OAEF,IAAM,EAAQ,CAAC,GAAG,KAAK,KAAK,EAC5B,KAAK,MAAQ,CAAC,EACd,IAAM,EAAM,GAAG,KAAK,wBACd,EAAO,KAAK,UAAU,CAAE,QAAS,KAAK,QAAS,OAAQ,CAAM,CAAC,EAC9D,EAAO,IAAI,KAAK,CAAC,CAAI,EAAG,CAAE,KAAM,kBAAmB,CAAC,EAC1D,EAAO,EAAK,CAAI,OAGZ,SAAQ,EAAkB,CAC9B,GAAI,KAAK,MAAM,SAAW,EACxB,OAEF,IAAM,EAAQ,CAAC,GAAG,KAAK,KAAK,EAC5B,KAAK,MAAQ,CAAC,EACd,GAAI,CACF,IAAM,EAAW,MAAM,KAAK,SAAS,gBAAiB,CACpD,QAAS,KAAK,QACd,OAAQ,CACV,CAAC,EACD,GAAI,CAAC,EAAS,GACZ,KAAK,IAAI,eAAgB,EAAS,MAAM,EAE1C,MAAO,EAAO,CACd,KAAK,IAAI,cAAe,CAAK,GAIzB,cAAc,CAAC,EAAqB,CAC1C,IAAM,EAAc,GAAQ,EAAY,EAClC,EAAM,EAAW,GAAK,GAAe,IAC3C,GAAI,IAAQ,KAAK,eACf,OAEF,KAAK,eAAiB,EACtB,KAAK,MAAM,KACT,KAAK,WACH,YACA,YACA,CACE,aAAc,EAChB,EACA,CAAE,KAAM,CAAY,CACtB,CACF,EACA,KAAK,cAAc,EAGrB,IAAI,CAAC,EAAqB,CACxB,KAAK,eAAiB,KACtB,KAAK,eAAe,CAAI,EAG1B,KAAK,CAAC,EAAmB,EAAkD,CACzE,KAAK,WAAW,EAAW,CAAU,EAGvC,aAAa,CAAC,EAA0C,EAAkD,CACxG,KAAK,WAAW,EAAW,CAAU,EAGvC,QAAQ,CAAC,EAAgB,EAA8C,CACrE,KAAK,MAAM,KACT,KAAK,WAAW,WAAY,WAAY,CACtC,OAAQ,GAAU,CAAC,EACnB,QACF,CAAC,CACH,EACA,KAAK,cAAc,EAGrB,kBAAkB,EAAoC,CACpD,OAAO,EAAsB,OAIzB,YAAW,CAAC,EAA6F,CAC7G,IAAM,EAAW,MAAM,KAAK,SAAS,iBAAkB,CACrD,QAAS,KAAK,QACd,UAAW,GAAa,EAAY,CACtC,CAAC,EACD,GAAI,CAAC,EAAS,GACZ,MAAU,MAAM,yBAAyB,EAAS,QAAQ,EAE5D,IAAM,EAAQ,MAAM,EAAS,KAAK,EAMlC,OADA,EAAY,GAAG,QAAQ,EAAiB,EAAK,YAAY,EAClD,EAGT,wBAAwB,EAAkB,CACxC,OAAO,EAAY,GAAG,QAAQ,CAAe,GAAK,KAIpD,WAAW,CAAC,EAAa,EAA+B,CACtD,IAAM,EAAQ,GAAgB,KAAK,yBAAyB,EACtD,EAAO,IAAI,IAAI,EAAK,EAAc,CAAC,EAEzC,GADA,EAAK,aAAa,IAAI,EAAY,KAAK,OAAO,EAC1C,EACF,EAAK,aAAa,IAAI,EAAe,CAAK,EAE5C,OAAO,EAAK,SAAS,OAIjB,cAAa,CAAC,EAAsB,EAAkD,CAC1F,IAAM,EAAW,MAAM,KAAK,SAAS,mBAAoB,CAAE,eAAc,SAAQ,CAAC,EAClF,GAAI,CAAC,EAAS,GACZ,MAAU,MAAM,2BAA2B,EAAS,QAAQ,EAIhE,OAAO,EAAS,CACd,IAAM,EAAM,EAAW,EACjB,EAAM,EAAa,EACzB,GAAI,GAAO,KAAK,eAAiB,KAAK,UAAU,eAAiB,KAAK,UAAU,cAC9E,EAAI,oBAAoB,QAAS,KAAK,aAAc,EAAI,EAE1D,GAAI,GAAO,KAAK,eAAiB,KAAK,UAAU,gBAC9C,EAAI,oBAAoB,SAAU,KAAK,cAAe,EAAI,EAE5D,GAAI,GAAO,KAAK,gBACd,EAAI,oBAAoB,WAAY,KAAK,eAAe,EAE1D,GAAI,GAAO,KAAK,gBACd,EAAI,oBAAoB,WAAY,KAAK,eAAe,EAE1D,GAAI,KAAK,WACP,aAAa,KAAK,UAAU,EAC5B,KAAK,WAAa,KAEpB,KAAK,iBAAiB,EACtB,KAAK,eAAiB,KACtB,KAAK,gBAAgB,EAEzB,CAEO,SAAS,CAAkB,CAAC,EAAsC,CACvE,OAAO,EAAa,KAAK,CAAI",
9
+ "debugId": "164F30813325DE1964756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,4 @@
1
+ var H=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],P=new Set(H),z=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),U={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function l(E){return P.has(E)}function J(E){let _=E.trim();if(_.length===0)return{eventName:_,canonicalEventName:_,isStandard:!1};let c=_.toLowerCase(),N=U[c];if(N)return{eventName:N,canonicalEventName:N,aliasOf:N,isStandard:!0};return{eventName:_,canonicalEventName:_,isStandard:l(_)}}function Q(E,_){if(_)return _;if(E==="page_view")return"page_view";if(E==="identify")return"identify";if(z.has(E))return"conversion";return"custom"}function j(){return[...H]}var C="bts_analytics_vid",k="bts_analytics_sid",Y="bts_analytics_jt",S="bts_analytics_attr",I="bts_site",O="bts_jt",w=300,y={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0},g=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"];function q(){return typeof window>"u"?null:window}function Z(){return typeof document>"u"?null:document}function D(){return q()?.localStorage??null}function G(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function L(E){return E!==null&&typeof E==="object"&&!Array.isArray(E)}function v(E){if(!E)return null;try{let _=JSON.parse(E);if(!L(_))return null;let c=L(_.utm)?Object.fromEntries(Object.entries(_.utm).filter((V)=>typeof V[1]==="string")):{},N=L(_.clickIds)?Object.fromEntries(Object.entries(_.clickIds).filter((V)=>typeof V[1]==="string")):{};return{utm:c,clickIds:N,landingUrl:typeof _.landingUrl==="string"?_.landingUrl:void 0,lastSeenAt:typeof _.lastSeenAt==="string"?_.lastSeenAt:new Date().toISOString(),referrer:typeof _.referrer==="string"?_.referrer:void 0}}catch{return null}}function W(){let E=D();return v(E?.getItem(S)??null)}function R(E){let _=D();if(!_)return;_.setItem(S,JSON.stringify(E))}function M(){let E=q();if(!E)return;return`${E.location.pathname}${E.location.search}`}function f(){return q()?.location.href}function K(){return q()?.location.origin??"https://behindthescenes.com"}function A(){return Z()?.referrer||void 0}function m(){let E=q();if(!E)return W();let _=new URLSearchParams(E.location.search),c={utm:{},clickIds:{},landingUrl:E.location.href,lastSeenAt:new Date().toISOString(),referrer:A()};for(let B of g){let X=_.get(B);if(!X)continue;if(B.startsWith("utm_")){c.utm[B]=X;continue}c.clickIds[B]=X}let N=W(),V={utm:{...N?.utm??{},...c.utm},clickIds:{...N?.clickIds??{},...c.clickIds},landingUrl:c.landingUrl??N?.landingUrl,lastSeenAt:c.lastSeenAt,referrer:c.referrer??N?.referrer};return R(V),V}function $(E){let _=E?.trim();return _?_.slice(0,512):void 0}function h(E){let _={...y};if(E.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1};if(E.autoTrack&&typeof E.autoTrack==="object")return{pageviews:E.autoTrack.pageviews??_.pageviews,history:E.autoTrack.history??_.history,outboundLinks:E.autoTrack.outboundLinks??_.outboundLinks,buttonClicks:E.autoTrack.buttonClicks??_.buttonClicks,formSubmissions:E.autoTrack.formSubmissions??_.formSubmissions};if(E.autoPageviews===!1)return{..._,pageviews:!1};if(E.autoPageviews===!0)return{..._,pageviews:!0};return _}class F{siteKey;endpoint;debug;flushIntervalMs;autoTrack;queue=[];flushTimer=null;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;constructor(E){if(this.siteKey=E.siteKey,this.endpoint=E.endpoint.replace(/\/$/,""),this.debug=E.debug??!1,this.flushIntervalMs=E.flushIntervalMs??w,this.autoTrack=h(E),m(),q())this.installAutoTracking()}static init(E){return new F(E)}getVisitorId(){let E=D(),_=E?.getItem(C);if(_)return _;let c=G();return E?.setItem(C,c),c}getSessionId(){let E=D(),_=E?.getItem(k);if(_)return _;let c=G();return E?.setItem(k,c),c}log(...E){if(this.debug)console.log("[@bts/analytics]",...E)}installAutoTracking(){let E=q(),_=Z();if(!E||!_)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(E),this.popstateHandler=()=>{this.recordPageView()},E.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(c)=>{this.handleAutoClick(c)},this.submitHandler=(c)=>{this.handleAutoSubmit(c)},this.pagehideHandler=()=>{this.flushWithBeacon()},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)_.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions)_.addEventListener("submit",this.submitHandler,!0);E.addEventListener("pagehide",this.pagehideHandler)}patchHistory(E){let _=E.history,c=_.pushState.bind(_),N=_.replaceState.bind(_),V=()=>{m(),this.recordPageView()};return _.pushState=(...B)=>{c(...B),V()},_.replaceState=(...B)=>{N(...B),V()},()=>{_.pushState=c,_.replaceState=N}}handleAutoClick(E){if(E.defaultPrevented)return;let _=E.target;if(!(_ instanceof Element))return;if(this.autoTrack.outboundLinks){let N=_.closest("a[href]");if(N instanceof HTMLAnchorElement){let V=$(N.href);if(!V)return;let B=new URL(V,K());if(B.origin!==K()){this.trackStandard("outbound_click",{elementHref:V,elementId:$(N.id),elementText:$(N.textContent),linkHost:B.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let c=_.closest("button,[role='button'],[data-bts-track-click]");if(!(c instanceof Element))return;this.trackStandard("button_click",{elementId:$(c.id),elementName:$(c.getAttribute("name")),elementRole:$(c.getAttribute("role")),elementText:$(c.textContent)})}handleAutoSubmit(E){if(E.defaultPrevented||!this.autoTrack.formSubmissions)return;let _=E.target;if(!(_ instanceof HTMLFormElement))return;this.trackStandard("form_submit",{formAction:$(_.action),formId:$(_.id),formMethod:$(_.method),formName:$(_.getAttribute("name"))})}buildEvent(E,_,c,N){let V=m(),B=N?.path??M(),X=A();return{eventName:E,eventType:_,path:B,referrer:X,occurredAt:new Date().toISOString(),visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...c??{},attribution:V?{utm:V.utm,clickIds:V.clickIds,landingUrl:V.landingUrl,referrer:V.referrer}:void 0,page:{title:Z()?.title,url:f()}}}}queueEvent(E,_,c){let N=J(E),V=Q(N.canonicalEventName,c),B={..._??{}};if(N.aliasOf)B.originalEventName=E;if(this.queue.push(this.buildEvent(N.eventName,V,B)),this.queue.length>=50){this.flushNow();return}this.scheduleFlush()}scheduleFlush(){if(this.flushTimer)return;this.flushTimer=setTimeout(()=>{this.flushTimer=null,this.flushNow()},this.flushIntervalMs)}async postJson(E,_){let c=`${this.endpoint}${E}`;return this.log("POST",c,_),fetch(c,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(_)})}flushWithBeacon(){if(this.queue.length===0)return;let E=q(),_=E?.navigator?.sendBeacon?.bind(E.navigator);if(!_)return;let c=[...this.queue];this.queue=[];let N=`${this.endpoint}/ingest/batch`,V=JSON.stringify({siteKey:this.siteKey,events:c}),B=new Blob([V],{type:"application/json"});_(N,B)}async flushNow(){if(this.queue.length===0)return;let E=[...this.queue];this.queue=[];try{let _=await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:E});if(!_.ok)this.log("flush failed",_.status)}catch(_){this.log("flush error",_)}}recordPageView(E){let _=E??M(),c=f()??_??"/";if(c===this.lastTrackedUrl)return;this.lastTrackedUrl=c,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:_})),this.scheduleFlush()}page(E){this.lastTrackedUrl=null,this.recordPageView(E)}track(E,_){this.queueEvent(E,_)}trackStandard(E,_){this.queueEvent(E,_)}identify(E,_){this.queue.push(this.buildEvent("identify","identify",{traits:_??{},userId:E})),this.scheduleFlush()}listStandardEvents(){return j()}async startFunnel(E){let _=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:E??M()});if(!_.ok)throw Error(`journey/start failed: ${_.status}`);let c=await _.json();return D()?.setItem(Y,c.journeyToken),c}getPersistedJourneyToken(){return D()?.getItem(Y)??null}decorateUrl(E,_){let c=_??this.getPersistedJourneyToken(),N=new URL(E,K());if(N.searchParams.set(I,this.siteKey),c)N.searchParams.set(O,c);return N.toString()}async notifyHandoff(E,_){let c=await this.postJson("/journey/handoff",{journeyToken:E,context:_});if(!c.ok)throw Error(`journey/handoff failed: ${c.status}`)}destroy(){let E=q(),_=Z();if(_&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))_.removeEventListener("click",this.clickHandler,!0);if(_&&this.submitHandler&&this.autoTrack.formSubmissions)_.removeEventListener("submit",this.submitHandler,!0);if(E&&this.popstateHandler)E.removeEventListener("popstate",this.popstateHandler);if(E&&this.pagehideHandler)E.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function T(E){return F.init(E)}export{j as listStandardWebEvents,T as createBTSAnalytics,F as BTSAnalytics};
2
+
3
+ //# debugId=BF4C211124B7A30C64756E2164756E21
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../src/events.ts", "../../src/index.ts"],
4
+ "sourcesContent": [
5
+ "export const STANDARD_WEB_EVENTS = [\n \"page_view\",\n \"view_content\",\n \"search\",\n \"lead\",\n \"sign_up\",\n \"begin_checkout\",\n \"add_payment_info\",\n \"purchase\",\n \"outbound_click\",\n \"button_click\",\n \"form_submit\",\n] as const;\n\nexport type BTSAnalyticsStandardEventName = (typeof STANDARD_WEB_EVENTS)[number];\n\nexport type BTSAnalyticsEventType = \"page_view\" | \"custom\" | \"identify\" | \"conversion\";\n\nconst STANDARD_EVENT_SET = new Set<string>(STANDARD_WEB_EVENTS);\n\nconst CONVERSION_EVENTS = new Set<string>([\"lead\", \"sign_up\", \"begin_checkout\", \"add_payment_info\", \"purchase\"]);\n\nconst LEGACY_EVENT_ALIASES: Record<string, BTSAnalyticsStandardEventName> = {\n $pageview: \"page_view\",\n checkout_started: \"begin_checkout\",\n lead_capture: \"lead\",\n purchase_completed: \"purchase\",\n registration_complete: \"sign_up\",\n};\n\nexport function isStandardWebEventName(eventName: string): eventName is BTSAnalyticsStandardEventName {\n return STANDARD_EVENT_SET.has(eventName);\n}\n\nexport function normalizeEventName(eventName: string): {\n eventName: string;\n canonicalEventName: string;\n aliasOf?: BTSAnalyticsStandardEventName;\n isStandard: boolean;\n} {\n const trimmed = eventName.trim();\n if (trimmed.length === 0) {\n return {\n eventName: trimmed,\n canonicalEventName: trimmed,\n isStandard: false,\n };\n }\n\n const lowered = trimmed.toLowerCase();\n const aliasOf = LEGACY_EVENT_ALIASES[lowered];\n if (aliasOf) {\n return {\n eventName: aliasOf,\n canonicalEventName: aliasOf,\n aliasOf,\n isStandard: true,\n };\n }\n\n return {\n eventName: trimmed,\n canonicalEventName: trimmed,\n isStandard: isStandardWebEventName(trimmed),\n };\n}\n\nexport function resolveEventType(eventName: string, explicitType?: BTSAnalyticsEventType): BTSAnalyticsEventType {\n if (explicitType) {\n return explicitType;\n }\n if (eventName === \"page_view\") {\n return \"page_view\";\n }\n if (eventName === \"identify\") {\n return \"identify\";\n }\n if (CONVERSION_EVENTS.has(eventName)) {\n return \"conversion\";\n }\n return \"custom\";\n}\n\nexport function listStandardWebEvents(): BTSAnalyticsStandardEventName[] {\n return [...STANDARD_WEB_EVENTS];\n}\n",
6
+ "import {\n listStandardWebEvents,\n normalizeEventName,\n resolveEventType,\n type BTSAnalyticsEventType,\n type BTSAnalyticsStandardEventName,\n} from \"./events\";\n\nconst STORAGE_VISITOR = \"bts_analytics_vid\";\nconst STORAGE_SESSION = \"bts_analytics_sid\";\nconst STORAGE_JOURNEY = \"bts_analytics_jt\";\nconst STORAGE_ATTRIBUTION = \"bts_analytics_attr\";\nconst QUERY_SITE = \"bts_site\";\nconst QUERY_JOURNEY = \"bts_jt\";\n\nconst DEFAULT_FLUSH_INTERVAL_MS = 300;\n\nconst DEFAULT_AUTO_TRACK = {\n pageviews: true,\n history: true,\n outboundLinks: true,\n buttonClicks: true,\n formSubmissions: true,\n} as const;\n\nconst ATTRIBUTION_QUERY_KEYS = [\n \"utm_source\",\n \"utm_medium\",\n \"utm_campaign\",\n \"utm_term\",\n \"utm_content\",\n \"fbclid\",\n \"gclid\",\n \"gbraid\",\n \"li_fat_id\",\n \"msclkid\",\n \"ttclid\",\n \"wbraid\",\n] as const;\n\ntype AutoTrackConfig = {\n pageviews: boolean;\n history: boolean;\n outboundLinks: boolean;\n buttonClicks: boolean;\n formSubmissions: boolean;\n};\n\nexport type BTSAnalyticsInit = {\n siteKey: string;\n endpoint: string;\n autoPageviews?: boolean;\n autoTrack?: boolean | Partial<AutoTrackConfig>;\n debug?: boolean;\n flushIntervalMs?: number;\n};\n\nexport type BTSAnalyticsPayloadProperties = Record<string, unknown>;\n\ntype StoredAttribution = {\n utm: Record<string, string>;\n clickIds: Record<string, string>;\n landingUrl?: string;\n lastSeenAt: string;\n referrer?: string;\n};\n\ntype QueuedAnalyticsEvent = {\n eventName: string;\n eventType: BTSAnalyticsEventType;\n path?: string;\n referrer?: string;\n occurredAt: string;\n visitorId: string;\n sessionId: string;\n properties: BTSAnalyticsPayloadProperties;\n};\n\nfunction safeWindow(): Window | null {\n return typeof window === \"undefined\" ? null : window;\n}\n\nfunction safeDocument(): Document | null {\n return typeof document === \"undefined\" ? null : document;\n}\n\nfunction safeStorage(): Storage | null {\n const win = safeWindow();\n return win?.localStorage ?? null;\n}\n\nfunction randomId(): string {\n if (typeof crypto !== \"undefined\" && typeof crypto.randomUUID === \"function\") {\n return crypto.randomUUID();\n }\n return `v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction parseStoredAttribution(raw: string | null): StoredAttribution | null {\n if (!raw) {\n return null;\n }\n try {\n const parsed = JSON.parse(raw) as unknown;\n if (!isRecord(parsed)) {\n return null;\n }\n const utm = isRecord(parsed.utm)\n ? Object.fromEntries(Object.entries(parsed.utm).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"))\n : {};\n const clickIds = isRecord(parsed.clickIds)\n ? Object.fromEntries(\n Object.entries(parsed.clickIds).filter((entry): entry is [string, string] => typeof entry[1] === \"string\"),\n )\n : {};\n return {\n utm,\n clickIds,\n landingUrl: typeof parsed.landingUrl === \"string\" ? parsed.landingUrl : undefined,\n lastSeenAt: typeof parsed.lastSeenAt === \"string\" ? parsed.lastSeenAt : new Date().toISOString(),\n referrer: typeof parsed.referrer === \"string\" ? parsed.referrer : undefined,\n };\n } catch {\n return null;\n }\n}\n\nfunction readStoredAttribution(): StoredAttribution | null {\n const storage = safeStorage();\n return parseStoredAttribution(storage?.getItem(STORAGE_ATTRIBUTION) ?? null);\n}\n\nfunction writeStoredAttribution(next: StoredAttribution): void {\n const storage = safeStorage();\n if (!storage) {\n return;\n }\n storage.setItem(STORAGE_ATTRIBUTION, JSON.stringify(next));\n}\n\nfunction currentPath(): string | undefined {\n const win = safeWindow();\n if (!win) {\n return undefined;\n }\n return `${win.location.pathname}${win.location.search}`;\n}\n\nfunction currentUrl(): string | undefined {\n return safeWindow()?.location.href;\n}\n\nfunction currentOrigin(): string {\n return safeWindow()?.location.origin ?? \"https://behindthescenes.com\";\n}\n\nfunction currentReferrer(): string | undefined {\n return safeDocument()?.referrer || undefined;\n}\n\nfunction readCurrentAttribution(): StoredAttribution | null {\n const win = safeWindow();\n if (!win) {\n return readStoredAttribution();\n }\n const params = new URLSearchParams(win.location.search);\n const next: StoredAttribution = {\n utm: {},\n clickIds: {},\n landingUrl: win.location.href,\n lastSeenAt: new Date().toISOString(),\n referrer: currentReferrer(),\n };\n for (const key of ATTRIBUTION_QUERY_KEYS) {\n const value = params.get(key);\n if (!value) {\n continue;\n }\n if (key.startsWith(\"utm_\")) {\n next.utm[key] = value;\n continue;\n }\n next.clickIds[key] = value;\n }\n\n const previous = readStoredAttribution();\n const merged: StoredAttribution = {\n utm: { ...(previous?.utm ?? {}), ...next.utm },\n clickIds: { ...(previous?.clickIds ?? {}), ...next.clickIds },\n landingUrl: next.landingUrl ?? previous?.landingUrl,\n lastSeenAt: next.lastSeenAt,\n referrer: next.referrer ?? previous?.referrer,\n };\n writeStoredAttribution(merged);\n return merged;\n}\n\nfunction sanitizeText(value: string | null | undefined): string | undefined {\n const trimmed = value?.trim();\n return trimmed ? trimmed.slice(0, 512) : undefined;\n}\n\nfunction createAutoTrackConfig(init: BTSAnalyticsInit): AutoTrackConfig {\n const base = { ...DEFAULT_AUTO_TRACK };\n if (init.autoTrack === false) {\n return {\n pageviews: false,\n history: false,\n outboundLinks: false,\n buttonClicks: false,\n formSubmissions: false,\n };\n }\n if (init.autoTrack && typeof init.autoTrack === \"object\") {\n return {\n pageviews: init.autoTrack.pageviews ?? base.pageviews,\n history: init.autoTrack.history ?? base.history,\n outboundLinks: init.autoTrack.outboundLinks ?? base.outboundLinks,\n buttonClicks: init.autoTrack.buttonClicks ?? base.buttonClicks,\n formSubmissions: init.autoTrack.formSubmissions ?? base.formSubmissions,\n };\n }\n if (init.autoPageviews === false) {\n return {\n ...base,\n pageviews: false,\n }\n }\n if (init.autoPageviews === true) {\n return {\n ...base,\n pageviews: true,\n }\n }\n return base;\n}\n\nexport class BTSAnalytics {\n private siteKey: string;\n private endpoint: string;\n private debug: boolean;\n private flushIntervalMs: number;\n private autoTrack: AutoTrackConfig;\n private queue: QueuedAnalyticsEvent[] = [];\n private flushTimer: ReturnType<typeof setTimeout> | null = null;\n private lastTrackedUrl: string | null = null;\n private unpatchHistory: (() => void) | null = null;\n private clickHandler: ((event: Event) => void) | null = null;\n private submitHandler: ((event: Event) => void) | null = null;\n private pagehideHandler: (() => void) | null = null;\n private popstateHandler: (() => void) | null = null;\n\n constructor(init: BTSAnalyticsInit) {\n this.siteKey = init.siteKey;\n this.endpoint = init.endpoint.replace(/\\/$/, \"\");\n this.debug = init.debug ?? false;\n this.flushIntervalMs = init.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n this.autoTrack = createAutoTrackConfig(init);\n\n readCurrentAttribution();\n\n if (safeWindow()) {\n this.installAutoTracking();\n }\n }\n\n static init(opts: BTSAnalyticsInit): BTSAnalytics {\n return new BTSAnalytics(opts);\n }\n\n getVisitorId(): string {\n const storage = safeStorage();\n const existing = storage?.getItem(STORAGE_VISITOR);\n if (existing) {\n return existing;\n }\n const id = randomId();\n storage?.setItem(STORAGE_VISITOR, id);\n return id;\n }\n\n getSessionId(): string {\n const storage = safeStorage();\n const existing = storage?.getItem(STORAGE_SESSION);\n if (existing) {\n return existing;\n }\n const id = randomId();\n storage?.setItem(STORAGE_SESSION, id);\n return id;\n }\n\n private log(...args: unknown[]) {\n if (this.debug) {\n // eslint-disable-next-line no-console\n console.log(\"[@bts/analytics]\", ...args);\n }\n }\n\n private installAutoTracking(): void {\n const win = safeWindow();\n const doc = safeDocument();\n if (!win || !doc) {\n return;\n }\n\n if (this.autoTrack.pageviews) {\n this.recordPageView();\n }\n\n if (this.autoTrack.history) {\n this.unpatchHistory = this.patchHistory(win);\n this.popstateHandler = () => {\n this.recordPageView();\n };\n win.addEventListener(\"popstate\", this.popstateHandler);\n }\n\n this.clickHandler = (event: Event) => {\n this.handleAutoClick(event);\n };\n this.submitHandler = (event: Event) => {\n this.handleAutoSubmit(event);\n };\n this.pagehideHandler = () => {\n this.flushWithBeacon();\n };\n\n if (this.autoTrack.outboundLinks || this.autoTrack.buttonClicks) {\n doc.addEventListener(\"click\", this.clickHandler, true);\n }\n if (this.autoTrack.formSubmissions) {\n doc.addEventListener(\"submit\", this.submitHandler, true);\n }\n win.addEventListener(\"pagehide\", this.pagehideHandler);\n }\n\n private patchHistory(win: Window): () => void {\n const history = win.history;\n const originalPushState = history.pushState.bind(history);\n const originalReplaceState = history.replaceState.bind(history);\n\n const onHistoryChange = () => {\n readCurrentAttribution();\n this.recordPageView();\n };\n\n history.pushState = ((...args: Parameters<History[\"pushState\"]>) => {\n originalPushState(...args);\n onHistoryChange();\n }) as History[\"pushState\"];\n\n history.replaceState = ((...args: Parameters<History[\"replaceState\"]>) => {\n originalReplaceState(...args);\n onHistoryChange();\n }) as History[\"replaceState\"];\n\n return () => {\n history.pushState = originalPushState;\n history.replaceState = originalReplaceState;\n };\n }\n\n private handleAutoClick(event: Event): void {\n if (event.defaultPrevented) {\n return;\n }\n const target = event.target;\n if (!(target instanceof Element)) {\n return;\n }\n\n if (this.autoTrack.outboundLinks) {\n const anchor = target.closest(\"a[href]\");\n if (anchor instanceof HTMLAnchorElement) {\n const href = sanitizeText(anchor.href);\n if (!href) {\n return;\n }\n const destination = new URL(href, currentOrigin());\n if (destination.origin !== currentOrigin()) {\n this.trackStandard(\"outbound_click\", {\n elementHref: href,\n elementId: sanitizeText(anchor.id),\n elementText: sanitizeText(anchor.textContent),\n linkHost: destination.hostname,\n });\n return;\n }\n }\n }\n\n if (!this.autoTrack.buttonClicks) {\n return;\n }\n\n const button = target.closest(\"button,[role='button'],[data-bts-track-click]\");\n if (!(button instanceof Element)) {\n return;\n }\n this.trackStandard(\"button_click\", {\n elementId: sanitizeText(button.id),\n elementName: sanitizeText(button.getAttribute(\"name\")),\n elementRole: sanitizeText(button.getAttribute(\"role\")),\n elementText: sanitizeText(button.textContent),\n });\n }\n\n private handleAutoSubmit(event: Event): void {\n if (event.defaultPrevented || !this.autoTrack.formSubmissions) {\n return;\n }\n const target = event.target;\n if (!(target instanceof HTMLFormElement)) {\n return;\n }\n this.trackStandard(\"form_submit\", {\n formAction: sanitizeText(target.action),\n formId: sanitizeText(target.id),\n formMethod: sanitizeText(target.method),\n formName: sanitizeText(target.getAttribute(\"name\")),\n });\n }\n\n private buildEvent(\n eventName: string,\n eventType: BTSAnalyticsEventType,\n properties?: BTSAnalyticsPayloadProperties,\n overrides?: { path?: string },\n ): QueuedAnalyticsEvent {\n const attribution = readCurrentAttribution();\n const path = overrides?.path ?? currentPath();\n const referrer = currentReferrer();\n return {\n eventName,\n eventType,\n path,\n referrer,\n occurredAt: new Date().toISOString(),\n visitorId: this.getVisitorId(),\n sessionId: this.getSessionId(),\n properties: {\n ...(properties ?? {}),\n attribution: attribution\n ? {\n utm: attribution.utm,\n clickIds: attribution.clickIds,\n landingUrl: attribution.landingUrl,\n referrer: attribution.referrer,\n }\n : undefined,\n page: {\n title: safeDocument()?.title,\n url: currentUrl(),\n },\n },\n };\n }\n\n private queueEvent(eventName: string, properties?: BTSAnalyticsPayloadProperties, explicitType?: BTSAnalyticsEventType): void {\n const normalized = normalizeEventName(eventName);\n const eventType = resolveEventType(normalized.canonicalEventName, explicitType);\n const nextProperties: BTSAnalyticsPayloadProperties = {\n ...(properties ?? {}),\n };\n if (normalized.aliasOf) {\n nextProperties.originalEventName = eventName;\n }\n this.queue.push(this.buildEvent(normalized.eventName, eventType, nextProperties));\n if (this.queue.length >= 50) {\n void this.flushNow();\n return;\n }\n this.scheduleFlush();\n }\n\n private scheduleFlush(): void {\n if (this.flushTimer) {\n return;\n }\n this.flushTimer = setTimeout(() => {\n this.flushTimer = null;\n void this.flushNow();\n }, this.flushIntervalMs);\n }\n\n private async postJson(path: string, body: unknown): Promise<Response> {\n const url = `${this.endpoint}${path}`;\n this.log(\"POST\", url, body);\n return fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n });\n }\n\n private flushWithBeacon(): void {\n if (this.queue.length === 0) {\n return;\n }\n const win = safeWindow();\n const beacon = win?.navigator?.sendBeacon?.bind(win.navigator);\n if (!beacon) {\n return;\n }\n const batch = [...this.queue];\n this.queue = [];\n const url = `${this.endpoint}/ingest/batch`;\n const body = JSON.stringify({ siteKey: this.siteKey, events: batch });\n const blob = new Blob([body], { type: \"application/json\" });\n beacon(url, blob);\n }\n\n async flushNow(): Promise<void> {\n if (this.queue.length === 0) {\n return;\n }\n const batch = [...this.queue];\n this.queue = [];\n try {\n const response = await this.postJson(\"/ingest/batch\", {\n siteKey: this.siteKey,\n events: batch,\n });\n if (!response.ok) {\n this.log(\"flush failed\", response.status);\n }\n } catch (error) {\n this.log(\"flush error\", error);\n }\n }\n\n private recordPageView(path?: string): void {\n const derivedPath = path ?? currentPath();\n const url = currentUrl() ?? derivedPath ?? \"/\";\n if (url === this.lastTrackedUrl) {\n return;\n }\n this.lastTrackedUrl = url;\n this.queue.push(\n this.buildEvent(\n \"page_view\",\n \"page_view\",\n {\n autoCaptured: true,\n },\n { path: derivedPath },\n ),\n );\n this.scheduleFlush();\n }\n\n page(path?: string): void {\n this.lastTrackedUrl = null;\n this.recordPageView(path);\n }\n\n track(eventName: string, properties?: BTSAnalyticsPayloadProperties): void {\n this.queueEvent(eventName, properties);\n }\n\n trackStandard(eventName: BTSAnalyticsStandardEventName, properties?: BTSAnalyticsPayloadProperties): void {\n this.queueEvent(eventName, properties);\n }\n\n identify(userId: string, traits?: BTSAnalyticsPayloadProperties): void {\n this.queue.push(\n this.buildEvent(\"identify\", \"identify\", {\n traits: traits ?? {},\n userId,\n }),\n );\n this.scheduleFlush();\n }\n\n listStandardEvents(): BTSAnalyticsStandardEventName[] {\n return listStandardWebEvents();\n }\n\n /** Start a cross-site funnel; persists journey token for decorateUrl. */\n async startFunnel(entryPath?: string): Promise<{ journeyId: string; journeyToken: string; expiresAt: number }> {\n const response = await this.postJson(\"/journey/start\", {\n siteKey: this.siteKey,\n entryPath: entryPath ?? currentPath(),\n });\n if (!response.ok) {\n throw new Error(`journey/start failed: ${response.status}`);\n }\n const data = (await response.json()) as {\n journeyId: string;\n journeyToken: string;\n expiresAt: number;\n };\n safeStorage()?.setItem(STORAGE_JOURNEY, data.journeyToken);\n return data;\n }\n\n getPersistedJourneyToken(): string | null {\n return safeStorage()?.getItem(STORAGE_JOURNEY) ?? null;\n }\n\n /** Append site key + journey token to a BTS URL (checkout, join, etc.). */\n decorateUrl(url: string, journeyToken?: string): string {\n const token = journeyToken ?? this.getPersistedJourneyToken();\n const next = new URL(url, currentOrigin());\n next.searchParams.set(QUERY_SITE, this.siteKey);\n if (token) {\n next.searchParams.set(QUERY_JOURNEY, token);\n }\n return next.toString();\n }\n\n /** Call from BTS after accepting the journey (optional; server can also infer). */\n async notifyHandoff(journeyToken: string, context?: Record<string, unknown>): Promise<void> {\n const response = await this.postJson(\"/journey/handoff\", { journeyToken, context });\n if (!response.ok) {\n throw new Error(`journey/handoff failed: ${response.status}`);\n }\n }\n\n destroy(): void {\n const win = safeWindow();\n const doc = safeDocument();\n if (doc && this.clickHandler && (this.autoTrack.outboundLinks || this.autoTrack.buttonClicks)) {\n doc.removeEventListener(\"click\", this.clickHandler, true);\n }\n if (doc && this.submitHandler && this.autoTrack.formSubmissions) {\n doc.removeEventListener(\"submit\", this.submitHandler, true);\n }\n if (win && this.popstateHandler) {\n win.removeEventListener(\"popstate\", this.popstateHandler);\n }\n if (win && this.pagehideHandler) {\n win.removeEventListener(\"pagehide\", this.pagehideHandler);\n }\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n this.unpatchHistory?.();\n this.unpatchHistory = null;\n this.flushWithBeacon();\n }\n}\n\nexport function createBTSAnalytics(init: BTSAnalyticsInit): BTSAnalytics {\n return BTSAnalytics.init(init);\n}\n\nexport { listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsStandardEventName };\n"
7
+ ],
8
+ "mappings": "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",
9
+ "debugId": "BF4C211124B7A30C64756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "@behindthescenes/analytics",
3
+ "version": "0.0.1",
4
+ "generatedAt": "2026-04-10T02:21:22.018Z",
5
+ "artifacts": {
6
+ "browser": "browser/browser.js",
7
+ "cjs": "cjs/index.js",
8
+ "esm": "esm/index.js",
9
+ "types": "types/index.d.ts"
10
+ }
11
+ }
@@ -0,0 +1,12 @@
1
+ import { BTSAnalytics, createBTSAnalytics, listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsInit, type BTSAnalyticsStandardEventName } from "./index";
2
+ declare global {
3
+ interface Window {
4
+ BTSAnalytics?: {
5
+ BTSAnalytics: typeof BTSAnalytics;
6
+ createBTSAnalytics: typeof createBTSAnalytics;
7
+ listStandardWebEvents: typeof listStandardWebEvents;
8
+ };
9
+ createBTSAnalytics?: typeof createBTSAnalytics;
10
+ }
11
+ }
12
+ export { BTSAnalytics, createBTSAnalytics, listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsInit, type BTSAnalyticsStandardEventName, };
@@ -0,0 +1,12 @@
1
+ export declare const STANDARD_WEB_EVENTS: readonly ["page_view", "view_content", "search", "lead", "sign_up", "begin_checkout", "add_payment_info", "purchase", "outbound_click", "button_click", "form_submit"];
2
+ export type BTSAnalyticsStandardEventName = (typeof STANDARD_WEB_EVENTS)[number];
3
+ export type BTSAnalyticsEventType = "page_view" | "custom" | "identify" | "conversion";
4
+ export declare function isStandardWebEventName(eventName: string): eventName is BTSAnalyticsStandardEventName;
5
+ export declare function normalizeEventName(eventName: string): {
6
+ eventName: string;
7
+ canonicalEventName: string;
8
+ aliasOf?: BTSAnalyticsStandardEventName;
9
+ isStandard: boolean;
10
+ };
11
+ export declare function resolveEventType(eventName: string, explicitType?: BTSAnalyticsEventType): BTSAnalyticsEventType;
12
+ export declare function listStandardWebEvents(): BTSAnalyticsStandardEventName[];
@@ -0,0 +1,67 @@
1
+ import { listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsStandardEventName } from "./events";
2
+ type AutoTrackConfig = {
3
+ pageviews: boolean;
4
+ history: boolean;
5
+ outboundLinks: boolean;
6
+ buttonClicks: boolean;
7
+ formSubmissions: boolean;
8
+ };
9
+ export type BTSAnalyticsInit = {
10
+ siteKey: string;
11
+ endpoint: string;
12
+ autoPageviews?: boolean;
13
+ autoTrack?: boolean | Partial<AutoTrackConfig>;
14
+ debug?: boolean;
15
+ flushIntervalMs?: number;
16
+ };
17
+ export type BTSAnalyticsPayloadProperties = Record<string, unknown>;
18
+ export declare class BTSAnalytics {
19
+ private siteKey;
20
+ private endpoint;
21
+ private debug;
22
+ private flushIntervalMs;
23
+ private autoTrack;
24
+ private queue;
25
+ private flushTimer;
26
+ private lastTrackedUrl;
27
+ private unpatchHistory;
28
+ private clickHandler;
29
+ private submitHandler;
30
+ private pagehideHandler;
31
+ private popstateHandler;
32
+ constructor(init: BTSAnalyticsInit);
33
+ static init(opts: BTSAnalyticsInit): BTSAnalytics;
34
+ getVisitorId(): string;
35
+ getSessionId(): string;
36
+ private log;
37
+ private installAutoTracking;
38
+ private patchHistory;
39
+ private handleAutoClick;
40
+ private handleAutoSubmit;
41
+ private buildEvent;
42
+ private queueEvent;
43
+ private scheduleFlush;
44
+ private postJson;
45
+ private flushWithBeacon;
46
+ flushNow(): Promise<void>;
47
+ private recordPageView;
48
+ page(path?: string): void;
49
+ track(eventName: string, properties?: BTSAnalyticsPayloadProperties): void;
50
+ trackStandard(eventName: BTSAnalyticsStandardEventName, properties?: BTSAnalyticsPayloadProperties): void;
51
+ identify(userId: string, traits?: BTSAnalyticsPayloadProperties): void;
52
+ listStandardEvents(): BTSAnalyticsStandardEventName[];
53
+ /** Start a cross-site funnel; persists journey token for decorateUrl. */
54
+ startFunnel(entryPath?: string): Promise<{
55
+ journeyId: string;
56
+ journeyToken: string;
57
+ expiresAt: number;
58
+ }>;
59
+ getPersistedJourneyToken(): string | null;
60
+ /** Append site key + journey token to a BTS URL (checkout, join, etc.). */
61
+ decorateUrl(url: string, journeyToken?: string): string;
62
+ /** Call from BTS after accepting the journey (optional; server can also infer). */
63
+ notifyHandoff(journeyToken: string, context?: Record<string, unknown>): Promise<void>;
64
+ destroy(): void;
65
+ }
66
+ export declare function createBTSAnalytics(init: BTSAnalyticsInit): BTSAnalytics;
67
+ export { listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsStandardEventName };
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@behindthescenes/analytics",
3
+ "version": "0.0.1",
4
+ "description": "Browser analytics SDK for BTS external-site event tracking and attribution.",
5
+ "license": "UNLICENSED",
6
+ "type": "module",
7
+ "main": "./dist/cjs/index.js",
8
+ "module": "./dist/esm/index.js",
9
+ "types": "./dist/types/index.d.ts",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/gwop-company/bts-backend.git",
13
+ "directory": "packages/analytics"
14
+ },
15
+ "homepage": "https://github.com/gwop-company/bts-backend/tree/main/packages/analytics#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/gwop-company/bts-backend/issues"
18
+ },
19
+ "keywords": [
20
+ "analytics",
21
+ "tracking",
22
+ "attribution",
23
+ "browser",
24
+ "sdk"
25
+ ],
26
+ "exports": {
27
+ ".": {
28
+ "types": "./dist/types/index.d.ts",
29
+ "import": "./dist/esm/index.js",
30
+ "require": "./dist/cjs/index.js",
31
+ "default": "./dist/esm/index.js"
32
+ },
33
+ "./browser": {
34
+ "types": "./dist/types/browser.d.ts",
35
+ "import": "./dist/browser/browser.js",
36
+ "default": "./dist/browser/browser.js"
37
+ },
38
+ "./package.json": "./package.json"
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "bun ./scripts/build.ts",
49
+ "build:hosted": "bun ./scripts/sync-hosted-bundle.ts",
50
+ "type-check": "tsc --noEmit",
51
+ "test": "bun test",
52
+ "lint": "eslint . --fix",
53
+ "lint:check": "eslint .",
54
+ "release:check": "bun run lint:check && bun run type-check && bun run test && bun run build && npm pack --dry-run",
55
+ "release:publish": "bun run release:check && npm publish --access public",
56
+ "prepublishOnly": "bun run release:check"
57
+ },
58
+ "devDependencies": {
59
+ "@bts/eslint-config": "workspace:*",
60
+ "@types/bun": "^1.3.11",
61
+ "eslint": "^9.17.0",
62
+ "typescript": "^5.8.3"
63
+ }
64
+ }