@behindthescenes/analytics 0.0.1 → 0.0.5
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 +169 -26
- package/dist/browser/browser.js +1 -4
- package/dist/cjs/index.js +1 -4
- package/dist/esm/index.js +1 -4
- package/dist/manifest.json +2 -2
- package/dist/types/browser.d.ts +5 -2
- package/dist/types/index.d.ts +72 -2
- package/package.json +4 -3
- package/dist/browser/browser.js.map +0 -12
- package/dist/cjs/index.js.map +0 -11
- package/dist/esm/index.js.map +0 -11
package/README.md
CHANGED
|
@@ -5,10 +5,58 @@ Browser SDK for BTS external-site analytics.
|
|
|
5
5
|
## What it does
|
|
6
6
|
|
|
7
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,
|
|
8
|
+
- Auto-tracks pageviews, SPA history changes, outbound clicks, button clicks, form submissions, safe GET search forms, and opt-in content views by default
|
|
9
9
|
- Supports custom client-side events with `track(...)`
|
|
10
10
|
- Preserves journey tokens, UTM params, and click IDs for downstream attribution and CAPI delivery
|
|
11
11
|
|
|
12
|
+
## Auto-tracked events
|
|
13
|
+
|
|
14
|
+
The SDK automatically captures events that can be detected safely from browser behavior:
|
|
15
|
+
|
|
16
|
+
- `page_view`: initial page load and SPA navigation
|
|
17
|
+
- `outbound_click`: links to external origins
|
|
18
|
+
- `button_click`: clicks on `button`, `[role="button"]`, or `[data-bts-track-click]`
|
|
19
|
+
- `form_submit`: form submissions
|
|
20
|
+
- `search`: GET search forms with common query keys (`q`, `query`, `search`)
|
|
21
|
+
- `view_content`: elements that opt in with `data-bts-view-content`
|
|
22
|
+
|
|
23
|
+
Opt into automatic content-view tracking by marking elements that represent content cards, offers, posts, videos, or products:
|
|
24
|
+
|
|
25
|
+
```html
|
|
26
|
+
<article
|
|
27
|
+
data-bts-view-content="offer_123"
|
|
28
|
+
data-bts-content-type="offer"
|
|
29
|
+
data-bts-content-title="Creator Accelerator"
|
|
30
|
+
>
|
|
31
|
+
Creator Accelerator
|
|
32
|
+
</article>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Identity, purchases, signups, payment steps, and arbitrary custom events are not auto-detected because they need product context. Call those explicitly after your app knows what happened.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// analytics.identify("user_123", { email: "fan@example.com" });
|
|
39
|
+
// analytics.track("watch_preview_clicked", { placement: "hero" });
|
|
40
|
+
// analytics.trackStandard("lead", { formId: "newsletter" });
|
|
41
|
+
// analytics.trackStandard("sign_up", { method: "email" });
|
|
42
|
+
// analytics.trackStandard("begin_checkout", { checkoutId: "offer_123" });
|
|
43
|
+
// analytics.trackStandard("add_payment_info", { checkoutId: "offer_123" });
|
|
44
|
+
// analytics.trackStandard("purchase", { orderId: "order_123", monetaryValue: 149 });
|
|
45
|
+
// const { journeyToken } = await analytics.startFunnel("/landing");
|
|
46
|
+
// const checkoutUrl = analytics.decorateUrl("https://behindthescenes.com/checkout", journeyToken);
|
|
47
|
+
// await analytics.flushNow();
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Auto pageviews and SPA navigation
|
|
51
|
+
|
|
52
|
+
- `**autoPageviews: false**` turns off the initial `page_view` **and** disables SPA history hooks (`pushState` / `replaceState` / `popstate`), so route changes do not emit automatic `page_view` events.
|
|
53
|
+
- To track SPA navigations without the first-load pageview, pass an `autoTrack` object, for example `{ pageviews: false, history: true, outboundLinks: true, buttonClicks: true, formSubmissions: true, search: true, viewContent: true }`.
|
|
54
|
+
|
|
55
|
+
## Flush reliability
|
|
56
|
+
|
|
57
|
+
- If a batch flush fails (network error or non-2xx response), events are **put back on the queue** and a later flush is scheduled (including the keepalive path used when `requestHeaders` is set).
|
|
58
|
+
- The default `sendBeacon` unload path cannot attach custom headers or observe success; use `requestHeaders` if you need the same retry semantics on `pagehide`.
|
|
59
|
+
|
|
12
60
|
## Install from npm
|
|
13
61
|
|
|
14
62
|
```bash
|
|
@@ -32,11 +80,88 @@ import { createBTSAnalytics } from "@behindthescenes/analytics";
|
|
|
32
80
|
|
|
33
81
|
const analytics = createBTSAnalytics({
|
|
34
82
|
siteKey: "your-public-site-key",
|
|
35
|
-
endpoint: "https://api.bts.dev/v2/website/analytics",
|
|
36
83
|
});
|
|
37
84
|
|
|
38
|
-
|
|
39
|
-
|
|
85
|
+
// Optional explicit-only examples:
|
|
86
|
+
// analytics.identify("user_123", { email: "fan@example.com" });
|
|
87
|
+
// analytics.track("video_played", { videoId: "intro-01" });
|
|
88
|
+
// analytics.trackStandard("lead", { formId: "newsletter" });
|
|
89
|
+
// analytics.trackStandard("begin_checkout", { checkoutId: "offer_123" });
|
|
90
|
+
// const { journeyToken } = await analytics.startFunnel("/landing");
|
|
91
|
+
// const checkoutUrl = analytics.decorateUrl("https://behindthescenes.com/checkout", journeyToken);
|
|
92
|
+
// await analytics.flushNow();
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Endpoint Override
|
|
96
|
+
|
|
97
|
+
The SDK defaults to the production BTS analytics endpoint. Override `endpoint` only for staging, local development, or a custom proxy.
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
const analytics = createBTSAnalytics({
|
|
101
|
+
siteKey: "your-public-site-key",
|
|
102
|
+
endpoint: "https://staging-api.bts.dev/v2/website/analytics",
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Contact forms
|
|
107
|
+
|
|
108
|
+
Use `submitContactForm(...)` when an external website needs to submit a simple contact enquiry into the space's GoHighLevel contact list.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
await analytics.submitContactForm({
|
|
112
|
+
locationId: "your-ghl-location-id",
|
|
113
|
+
email: "fan@example.com",
|
|
114
|
+
subject: "Partnership enquiry",
|
|
115
|
+
body: "Tell us more about working together.",
|
|
116
|
+
});
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Required fields:
|
|
120
|
+
|
|
121
|
+
- `locationId`: the GoHighLevel location configured for the BTS space
|
|
122
|
+
- `email`: the visitor's email address
|
|
123
|
+
- `subject`: short enquiry subject
|
|
124
|
+
- `body`: the message or "tell us more" text
|
|
125
|
+
|
|
126
|
+
Optional fields:
|
|
127
|
+
|
|
128
|
+
- `name`, `firstName`, `lastName`: contact identity fields
|
|
129
|
+
- `source`: defaults to `behind_the_scenes` when omitted
|
|
130
|
+
- `tags`: extra GHL tags to attach to the contact
|
|
131
|
+
- `customFields`: GHL custom fields to pass through directly
|
|
132
|
+
- `metadata`: extra BTS metadata; values are forwarded as `bts_meta_<key>` contact fields
|
|
133
|
+
|
|
134
|
+
`subject` and `body` are also stored as BTS metadata fields on the GHL contact (`bts_meta_subject` and `bts_meta_body`). This helper creates or updates the contact only; it does not create a GHL opportunity.
|
|
135
|
+
|
|
136
|
+
The SDK uses the production website analytics endpoint by default:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
const analytics = createBTSAnalytics({
|
|
140
|
+
siteKey: "your-public-site-key",
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`submitContactForm(...)` derives the sibling contact endpoint from the configured analytics URL and posts to `/v2/website/ghl/leads`.
|
|
145
|
+
|
|
146
|
+
Example form wiring:
|
|
147
|
+
|
|
148
|
+
```ts
|
|
149
|
+
const form = document.querySelector<HTMLFormElement>("#contact-form");
|
|
150
|
+
|
|
151
|
+
form?.addEventListener("submit", async (event) => {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
|
|
154
|
+
const data = new FormData(form);
|
|
155
|
+
await analytics.submitContactForm({
|
|
156
|
+
locationId: "your-ghl-location-id",
|
|
157
|
+
email: String(data.get("email") ?? ""),
|
|
158
|
+
subject: String(data.get("subject") ?? ""),
|
|
159
|
+
body: String(data.get("body") ?? ""),
|
|
160
|
+
name: String(data.get("name") ?? ""),
|
|
161
|
+
metadata: {
|
|
162
|
+
page: window.location.pathname,
|
|
163
|
+
},
|
|
164
|
+
});
|
|
40
165
|
});
|
|
41
166
|
```
|
|
42
167
|
|
|
@@ -47,14 +172,31 @@ analytics.track("video_played", {
|
|
|
47
172
|
import { createBTSAnalytics } from "https://app.behindthescenes.com/sdk/analytics/latest/browser/browser.js";
|
|
48
173
|
|
|
49
174
|
window.btsAnalytics = createBTSAnalytics({
|
|
50
|
-
siteKey: "your-public-site-key"
|
|
51
|
-
endpoint: "https://api.bts.dev/v2/website/analytics"
|
|
175
|
+
siteKey: "your-public-site-key"
|
|
52
176
|
});
|
|
53
177
|
</script>
|
|
54
178
|
```
|
|
55
179
|
|
|
56
180
|
The hosted bundle is generated from `packages/analytics/dist` and synced into `apps/bts-web/public/sdk/analytics`.
|
|
57
181
|
|
|
182
|
+
Release builds do not emit or publish source maps. The package build fails if any `.map` files are present in `dist`.
|
|
183
|
+
|
|
184
|
+
## Releases
|
|
185
|
+
|
|
186
|
+
Publishing the npm package bumps the version with standard semver release types:
|
|
187
|
+
|
|
188
|
+
- `patch`: bug fixes and backwards-compatible corrections, for example `1.2.3` to `1.2.4`
|
|
189
|
+
- `minor`: backwards-compatible features, for example `1.2.3` to `1.3.0`
|
|
190
|
+
- `major`: breaking changes, for example `1.2.3` to `2.0.0`
|
|
191
|
+
|
|
192
|
+
Run the GitHub Actions release workflow manually and choose the release type to publish the next package version. The workflow reads the latest published npm version, increments it, validates the package, publishes it, and commits the updated `package.json` version.
|
|
193
|
+
|
|
194
|
+
For local publishing, set the release type before running the publish script:
|
|
195
|
+
|
|
196
|
+
```sh
|
|
197
|
+
RELEASE_TYPE=minor bun run release:publish
|
|
198
|
+
```
|
|
199
|
+
|
|
58
200
|
## Standard events
|
|
59
201
|
|
|
60
202
|
```ts
|
|
@@ -94,31 +236,32 @@ analytics.track("cta_clicked", {
|
|
|
94
236
|
});
|
|
95
237
|
```
|
|
96
238
|
|
|
97
|
-
##
|
|
239
|
+
## Request signing headers
|
|
98
240
|
|
|
99
|
-
|
|
100
|
-
const { journeyToken } = await analytics.startFunnel();
|
|
101
|
-
const checkoutUrl = analytics.decorateUrl("https://behindthescenes.com/checkout", journeyToken);
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
## Scripts
|
|
241
|
+
Use `requestHeaders` to attach custom anti-spoof headers to every API request. The hook receives the serialized request body, so you can sign the exact payload being sent.
|
|
105
242
|
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
243
|
+
```ts
|
|
244
|
+
const analytics = createBTSAnalytics({
|
|
245
|
+
siteKey: "your-public-site-key",
|
|
246
|
+
requestHeaders: async ({ bodyText, headers, path }) => {
|
|
247
|
+
const timestamp = new Date().toISOString();
|
|
248
|
+
const signature = await signAnalyticsPayload(`${timestamp}:${path}:${bodyText}`);
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
...headers,
|
|
252
|
+
"X-BTS-Analytics-Timestamp": timestamp,
|
|
253
|
+
"X-BTS-Analytics-Signature": signature,
|
|
254
|
+
};
|
|
255
|
+
},
|
|
256
|
+
});
|
|
110
257
|
```
|
|
111
258
|
|
|
112
|
-
`
|
|
259
|
+
When `requestHeaders` is configured, page-unload flushes use `fetch(..., { keepalive: true })` instead of `sendBeacon`, because browsers do not allow custom headers on beacon requests.
|
|
113
260
|
|
|
114
|
-
##
|
|
261
|
+
## Journeys
|
|
115
262
|
|
|
116
|
-
```
|
|
117
|
-
|
|
263
|
+
```ts
|
|
264
|
+
const { journeyToken } = await analytics.startFunnel();
|
|
265
|
+
const checkoutUrl = analytics.decorateUrl("https://behindthescenes.com/checkout", journeyToken);
|
|
118
266
|
```
|
|
119
267
|
|
|
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
|
-
|
package/dist/browser/browser.js
CHANGED
|
@@ -1,4 +1 @@
|
|
|
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
|
|
1
|
+
var J=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],g=new Set(J),u=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 m(S){return g.has(S)}function X(S){let A=S.trim();if(A.length===0)return{eventName:A,canonicalEventName:A,isStandard:!1};let f=A.toLowerCase(),c=v[f];if(c)return{eventName:c,canonicalEventName:c,aliasOf:c,isStandard:!0};return{eventName:A,canonicalEventName:A,isStandard:m(A)}}function Z(S,A){if(A)return A;if(S==="page_view")return"page_view";if(S==="identify")return"identify";if(u.has(S))return"conversion";return"custom"}function C(){return[...J]}var j="bts_analytics_vid",G="bts_analytics_sid",M="bts_analytics_jt",O="bts_analytics_jid",U="bts_analytics_attr",b="bts_site",h="bts_jt",l=300,s="https://api.bts.dev/v2/website/analytics",d={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},Q=["q","query","search"],p=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"],a=["_fbp","_fbc"];function N(){return typeof window>"u"?null:window}function D(){return typeof document>"u"?null:document}function L(){return N()?.localStorage??null}function V(){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(S){return S!==null&&typeof S==="object"&&!Array.isArray(S)}function n(S){if(!S)return null;try{let A=JSON.parse(S);if(!q(A))return null;let f=q(A.utm)?Object.fromEntries(Object.entries(A.utm).filter((B)=>typeof B[1]==="string")):{},c=q(A.clickIds)?Object.fromEntries(Object.entries(A.clickIds).filter((B)=>typeof B[1]==="string")):{};return{utm:f,clickIds:c,landingUrl:typeof A.landingUrl==="string"?A.landingUrl:void 0,lastSeenAt:typeof A.lastSeenAt==="string"?A.lastSeenAt:new Date().toISOString(),referrer:typeof A.referrer==="string"?A.referrer:void 0}}catch{return null}}function K(){let S=L();return n(S?.getItem(U)??null)}function o(S){let A=L();if(!A)return;A.setItem(U,JSON.stringify(S))}function F(){let S=N();if(!S)return;return`${S.location.pathname}${S.location.search}`}function H(){return N()?.location.href}function R(){return N()?.location.origin??"https://behindthescenes.com"}function P(){return D()?.referrer||void 0}function z(S){let A=D();if(!A?.cookie)return;let f=`${S}=`,c=A.cookie.split(";").map((E)=>E.trim()).find((E)=>E.startsWith(f));if(!c)return;let B=c.slice(f.length);if(B==="")return;try{return decodeURIComponent(B)}catch{return B}}function r(){let S=z("_ga");if(!S)return;let A=S.split(".");if(A.length>=4&&/^GA\d+$/i.test(A[0]??""))return A.slice(-2).join(".");return _(S)}function t(){let A=D()?.querySelector?.('script[src*="googletagmanager.com/gtag/js?id="]'),f=A&&"src"in A&&typeof A.src==="string"?A.src:void 0;if(!f)return;try{return _(new URL(f).searchParams.get("id"))}catch{return}}function i(){let S=N(),A=r(),f=t(),c=typeof S?.gtag==="function"||!!f||!!A;if(!c&&!A&&!f)return;return{tagInstalled:c,clientId:A,measurementId:f}}function k(S){if(!S.googleAnalytics||typeof S.googleAnalytics==="boolean")return;return _(S.googleAnalytics.measurementId)}function e(S){if(!S.googleAnalytics)return!1;if(S.googleAnalytics===!0)return!1;return S.googleAnalytics.loadTag!==!1&&!!k(S)}function SS(S){let A=N(),f=D();if(!A||!f)return;A.dataLayer=Array.isArray(A.dataLayer)?A.dataLayer:[],A.gtag??=(...B)=>{A.dataLayer?.push(B)};let c=`script[src*="googletagmanager.com/gtag/js?id=${S}"]`;if(!f.querySelector?.(c)){let B=f.createElement("script");B.async=!0,B.src=`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(S)}`,B.setAttribute("data-bts-ga-proxy","true"),(f.head??f.documentElement).appendChild(B)}A.gtag("js",new Date),A.gtag("config",S,{send_page_view:!1})}function AS(){let S=N();if(!S)return;let A=typeof Intl<"u"?_(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,f=S.navigator,c=_(f?.language),B=Array.isArray(f?.languages)?f.languages.filter((y)=>typeof y==="string").slice(0,10):[],E=_(f?.userAgent),T={timezone:A,language:c,languages:B.length>0?B:void 0,userAgent:E};return Object.values(T).some((y)=>y!==void 0)?T:void 0}function Y(S){let A=$(),f=i();return{event_id:S,ga_client_id:typeof f?.clientId==="string"?f.clientId:void 0,ga_tag_installed:typeof f?.tagInstalled==="boolean"?f.tagInstalled:void 0,attribution:A?{utm:A.utm,clickIds:A.clickIds,...A.clickIds,landingUrl:A.landingUrl,referrer:A.referrer}:void 0,client:AS(),googleAnalytics:f,page:{title:D()?.title,url:H()}}}function $(){let S=N();if(!S)return K();let A=new URLSearchParams(S.location.search),f={utm:{},clickIds:{},landingUrl:S.location.href,lastSeenAt:new Date().toISOString(),referrer:P()};for(let E of p){let T=A.get(E);if(!T)continue;if(E.startsWith("utm_")){f.utm[E]=T;continue}f.clickIds[E]=T}for(let E of a){let T=z(E);if(T)f.clickIds[E.replace(/^_/,"")]=T}let c=K(),B={utm:{...c?.utm??{},...f.utm},clickIds:{...c?.clickIds??{},...f.clickIds},landingUrl:f.landingUrl??c?.landingUrl,lastSeenAt:f.lastSeenAt,referrer:f.referrer??c?.referrer};return o(B),B}function _(S){let A=S?.trim();return A?A.slice(0,512):void 0}function fS(S){if(!S)return{};if(S instanceof Headers){let A={};return S.forEach((f,c)=>{A[c]=f}),A}if(Array.isArray(S))return Object.fromEntries(S.map(([A,f])=>[A,String(f)]));return Object.fromEntries(Object.entries(S).map(([A,f])=>[A,String(f)]))}function cS(S){let A={...d};if(S.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(S.autoTrack&&typeof S.autoTrack==="object")return{pageviews:S.autoTrack.pageviews??A.pageviews,history:S.autoTrack.pageviews===!1?!1:S.autoTrack.history??A.history,outboundLinks:S.autoTrack.outboundLinks??A.outboundLinks,buttonClicks:S.autoTrack.buttonClicks??A.buttonClicks,formSubmissions:S.autoTrack.formSubmissions??A.formSubmissions,search:S.autoTrack.search??A.search,viewContent:S.autoTrack.viewContent??A.viewContent};if(S.autoPageviews===!1)return{...A,pageviews:!1,history:!1};if(S.autoPageviews===!0)return{...A,pageviews:!0};return A}class W{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;viewContentMutationObserver=null;viewContentRescanPending=!1;viewContentListenersTornDown=!1;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(S){if(this.siteKey=S.siteKey,this.endpoint=(S.endpoint??s).replace(/\/$/,""),this.debug=S.debug??!1,this.flushIntervalMs=S.flushIntervalMs??l,this.autoTrack=cS(S),this.requestHeaders=S.requestHeaders,e(S)){let A=k(S);if(A)SS(A)}if($(),N())this.installAutoTracking()}static init(S){return new W(S)}getVisitorId(){let S=L(),A=S?.getItem(j);if(A)return A;let f=V();return S?.setItem(j,f),f}getSessionId(){let S=L(),A=S?.getItem(G);if(A)return A;let f=V();return S?.setItem(G,f),f}getPersistedJourneyId(){return L()?.getItem(O)??null}log(...S){if(this.debug)console.log("[@bts/analytics]",...S)}installAutoTracking(){let S=N(),A=D();if(!S||!A)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(S),this.popstateHandler=()=>{this.recordPageView(),this.rescanViewContentAfterNavigation()},S.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(f)=>{this.handleAutoClick(f)},this.submitHandler=(f)=>{this.handleAutoSubmit(f)},this.pagehideHandler=()=>{this.viewContentListenersTornDown=!0,this.flushWithBeacon(),this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)A.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)A.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(A);S.addEventListener("pagehide",this.pagehideHandler)}getOrCreateViewContentIntersectionObserver(){if(typeof IntersectionObserver>"u"||this.viewContentListenersTornDown)return null;if(this.viewContentObserver)return this.viewContentObserver;return this.viewContentObserver=new IntersectionObserver((S,A)=>{for(let f of S){if(!f.isIntersecting||!(f.target instanceof Element))continue;this.trackViewContentElement(f.target),A.unobserve(f.target)}}),this.viewContentObserver}observeViewContentElements(S){if(this.viewContentListenersTornDown||this.destroyed)return;let A=this.getOrCreateViewContentIntersectionObserver();if(!A)return;for(let f of Array.from(S.querySelectorAll("[data-bts-view-content]"))){if(this.observedViewContentElements.has(f))continue;this.observedViewContentElements.add(f),A.observe(f)}}scheduleViewContentRescanFromMutations(){if(this.viewContentListenersTornDown)return;if(this.viewContentRescanPending)return;this.viewContentRescanPending=!0,queueMicrotask(()=>{if(this.viewContentRescanPending=!1,this.viewContentListenersTornDown||this.destroyed)return;let S=D();if(S)this.observeViewContentElements(S)})}rescanViewContentAfterNavigation(){if(!this.autoTrack.viewContent)return;if(this.viewContentListenersTornDown)return;let S=D();if(S)this.observeViewContentElements(S)}installViewContentTracking(S){if(typeof IntersectionObserver>"u")return;if(this.observeViewContentElements(S),typeof MutationObserver>"u")return;let A=S.body??S.documentElement;if(!A)return;this.viewContentMutationObserver=new MutationObserver(()=>{this.scheduleViewContentRescanFromMutations()}),this.viewContentMutationObserver.observe(A,{childList:!0,subtree:!0})}trackViewContentElement(S){if(this.trackedViewContentElements.has(S))return;this.trackedViewContentElements.add(S);let A=_(S.getAttribute("data-bts-view-content")||S.getAttribute("data-bts-content-id")||S.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:A,contentTitle:_(S.getAttribute("data-bts-content-title")||S.textContent),contentType:_(S.getAttribute("data-bts-content-type")),elementId:_(S.id)})}patchHistory(S){let A=S.history,f=A.pushState.bind(A),c=A.replaceState.bind(A),B=()=>{$(),this.recordPageView(),this.rescanViewContentAfterNavigation()};return A.pushState=(...E)=>{f(...E),B()},A.replaceState=(...E)=>{c(...E),B()},()=>{A.pushState=f,A.replaceState=c}}handleAutoClick(S){if(S.defaultPrevented)return;let A=S.target;if(!(A instanceof Element))return;if(this.autoTrack.outboundLinks){let c=A.closest("a[href]");if(c instanceof HTMLAnchorElement){let B=_(c.href);if(!B)return;let E=new URL(B,R());if(E.origin!==R()){this.trackStandard("outbound_click",{elementHref:B,elementId:_(c.id),elementText:_(c.textContent),linkHost:E.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let f=A.closest("button,[role='button'],[data-bts-track-click]");if(!(f instanceof Element))return;this.trackStandard("button_click",{elementId:_(f.id),elementName:_(f.getAttribute("name")),elementRole:_(f.getAttribute("role")),elementText:_(f.textContent)})}handleAutoSubmit(S){if(S.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let A=S.target;if(!(A instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(A);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:_(A.action),formId:_(A.id),formMethod:_(A.method),formName:_(A.getAttribute("name"))})}trackSearchForm(S){if((S.method||"get").toLowerCase()!=="get")return;let f=this.extractSearchQuery(S);if(!f)return;this.trackStandard("search",{autoCaptured:!0,formAction:_(S.action),formId:_(S.id),formName:_(S.getAttribute("name")),queryKey:f.key,searchQuery:f.value})}extractSearchQuery(S){try{let f=new FormData(S);for(let c of Q){let B=f.get(c);if(typeof B==="string"){let E=_(B);if(E)return{key:c,value:E}}}}catch{}let A=S.elements;for(let f of Q){let c=A?.namedItem?.(f),B=c&&"value"in c?c.value:void 0;if(typeof B==="string"){let E=_(B);if(E)return{key:f,value:E}}}return null}buildEvent(S,A,f,c){let B=c?.path??F(),E=P(),T=V(),y=Y(T);return{eventName:S,eventType:A,path:B,referrer:E,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...f??{},...y,event_id:f?.event_id??f?.eventId??T}}}queueEvent(S,A,f){let c=X(S),B=Z(c.canonicalEventName,f),E={...A??{}};if(c.aliasOf)E.originalEventName=S;if(this.queue.push(this.buildEvent(c.eventName,B,E)),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(S,A){let f=`${this.endpoint}${S}`;this.log("POST",f,A);let c=JSON.stringify(A),B=await this.resolveRequestHeaders(S,f,A,c,this.endpoint);return fetch(f,{method:"POST",headers:B,body:c})}async postWebsiteJson(S,A){let f=this.endpoint.replace(/\/analytics$/,""),c=`${f}${S}`;this.log("POST",c,A);let B=JSON.stringify(A),E=await this.resolveRequestHeaders(S,c,A,B,f);return fetch(c,{method:"POST",headers:E,body:B})}async postJsonKeepalive(S,A){let f=`${this.endpoint}${S}`;this.log("POST keepalive",f,A);let c=JSON.stringify(A),B=await this.resolveRequestHeaders(S,f,A,c,this.endpoint);return fetch(f,{method:"POST",headers:B,body:c,keepalive:!0})}async resolveRequestHeaders(S,A,f,c,B=this.endpoint){let E={"Content-Type":"application/json"};if(!this.requestHeaders)return E;let T=typeof this.requestHeaders==="function"?await this.requestHeaders({body:f,bodyText:c,endpoint:B,headers:{...E},path:S,siteKey:this.siteKey,url:A}):this.requestHeaders;return{...E,...fS(T)}}async flushBatch(S,A=!1){try{let f=A?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:S}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:S});if(!f.ok)return this.log("flush failed",f.status),!1;return!0}catch(f){return this.log("flush error",f),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let T=[...this.queue];this.queue=[],this.flushBatch(T,!0).then((y)=>{if(!y&&!this.destroyed)this.queue.unshift(...T),this.scheduleFlush()});return}let S=N(),A=S?.navigator?.sendBeacon?.bind(S.navigator);if(!A)return;let f=[...this.queue];this.queue=[];let c=`${this.endpoint}/ingest/batch`,B=JSON.stringify({siteKey:this.siteKey,events:f}),E=new Blob([B],{type:"application/json"});A(c,E)}async flushNow(){if(this.queue.length===0)return;let S=[...this.queue];if(this.queue=[],!await this.flushBatch(S)&&!this.destroyed)this.queue.unshift(...S),this.scheduleFlush()}recordPageView(S){let A=S??F(),f=H()??A??"/";if(f===this.lastTrackedUrl)return;this.lastTrackedUrl=f,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:A})),this.scheduleFlush()}page(S){this.lastTrackedUrl=null,this.recordPageView(S)}track(S,A){this.queueEvent(S,A)}trackStandard(S,A){this.queueEvent(S,A)}identify(S,A){this.queue.push(this.buildEvent("identify","identify",{traits:A??{},userId:S})),this.scheduleFlush()}listStandardEvents(){return C()}async submitContactForm(S){let A=V(),f=Y(A),c=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:S.locationId,email:S.email,phone:S.phone,subject:S.subject,body:S.body,name:S.name,firstName:S.firstName,lastName:S.lastName,source:S.source,tags:S.tags,customFields:S.customFields,metadata:{...S.metadata??{},...f}});if(!c.ok)throw Error(`contact form submit failed: ${c.status}`);return await c.json()}async startFunnel(S){let A=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:S??F()});if(!A.ok)throw Error(`journey/start failed: ${A.status}`);let f=await A.json();return L()?.setItem(M,f.journeyToken),L()?.setItem(O,f.journeyId),f}getPersistedJourneyToken(){return L()?.getItem(M)??null}decorateUrl(S,A){let f=A??this.getPersistedJourneyToken(),c=new URL(S,R());if(c.searchParams.set(b,this.siteKey),f)c.searchParams.set(h,f);return c.toString()}async notifyHandoff(S,A){let f=await this.postJson("/journey/handoff",{journeyToken:S,context:A});if(!f.ok)throw Error(`journey/handoff failed: ${f.status}`)}destroy(){this.destroyed=!0,this.viewContentListenersTornDown=!0;let S=N(),A=D();if(A&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))A.removeEventListener("click",this.clickHandler,!0);if(A&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))A.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null,S&&this.popstateHandler)S.removeEventListener("popstate",this.popstateHandler);if(S&&this.pagehideHandler)S.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function w(S){return W.init(S)}function I(S){return typeof S==="object"&&S!==null&&!Array.isArray(S)}function BS(S){return Array.from(S)}function x(S){let A=typeof window>"u"?null:window;if(!A)return;let[f,c,B]=S;if(typeof f!=="string")return;if(f==="js")return;if(f==="config"){if(typeof c!=="string"||c.trim().length===0)return;let y=I(B)?B:{};A.btsAnalytics=w({...y,siteKey:c});return}let E=A.btsAnalytics;if(!E)return;let T=I(B)?B:void 0;if(f==="event"&&typeof c==="string"){E.track(c,T);return}if(f==="identify"&&typeof c==="string"){E.identify(c,T);return}if(f==="page"){E.page(typeof c==="string"?c:void 0);return}if(f==="flush")E.flushNow()}if(typeof window<"u"){let S=Array.isArray(window.btsDataLayer)?[...window.btsDataLayer]:[];window.btsDataLayer=Array.isArray(window.btsDataLayer)?window.btsDataLayer:[],window.BTSAnalytics={BTSAnalytics:W,createBTSAnalytics:w,listStandardWebEvents:C},window.createBTSAnalytics=w;for(let A of S)x(BS(A));window.bts=(...A)=>{window.btsDataLayer?.push(A),x(A)}}export{C as listStandardWebEvents,w as createBTSAnalytics,W as BTSAnalytics};
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,4 +1 @@
|
|
|
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
|
|
1
|
+
var{defineProperty:q,getOwnPropertyNames:g,getOwnPropertyDescriptor:v}=Object,h=Object.prototype.hasOwnProperty;var j=new WeakMap,b=(_)=>{var E=j.get(_),S;if(E)return E;if(E=q({},"__esModule",{value:!0}),_&&typeof _==="object"||typeof _==="function")g(_).map((N)=>!h.call(E,N)&&q(E,N,{get:()=>_[N],enumerable:!(S=v(_,N))||S.enumerable}));return j.set(_,E),E};var m=(_,E)=>{for(var S in E)q(_,S,{get:E[S],enumerable:!0,configurable:!0,set:(N)=>E[S]=()=>N})};var $_={};m($_,{listStandardWebEvents:()=>G,createBTSAnalytics:()=>L_,BTSAnalytics:()=>F});module.exports=b($_);var A=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],y=new Set(A),u=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),l={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function p(_){return y.has(_)}function Y(_){let E=_.trim();if(E.length===0)return{eventName:E,canonicalEventName:E,isStandard:!1};let S=E.toLowerCase(),N=l[S];if(N)return{eventName:N,canonicalEventName:N,aliasOf:N,isStandard:!0};return{eventName:E,canonicalEventName:E,isStandard:p(E)}}function f(_,E){if(E)return E;if(_==="page_view")return"page_view";if(_==="identify")return"identify";if(u.has(_))return"conversion";return"custom"}function G(){return[...A]}var K="bts_analytics_vid",U="bts_analytics_sid",R="bts_analytics_jt",H="bts_analytics_jid",k="bts_analytics_attr",d="bts_site",s="bts_jt",o=300,a="https://api.bts.dev/v2/website/analytics",r={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},P=["q","query","search"],n=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"],i=["_fbp","_fbc"];function L(){return typeof window>"u"?null:window}function $(){return typeof document>"u"?null:document}function J(){return L()?.localStorage??null}function Z(){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(_){return _!==null&&typeof _==="object"&&!Array.isArray(_)}function t(_){if(!_)return null;try{let E=JSON.parse(_);if(!M(E))return null;let S=M(E.utm)?Object.fromEntries(Object.entries(E.utm).filter((V)=>typeof V[1]==="string")):{},N=M(E.clickIds)?Object.fromEntries(Object.entries(E.clickIds).filter((V)=>typeof V[1]==="string")):{};return{utm:S,clickIds:N,landingUrl:typeof E.landingUrl==="string"?E.landingUrl:void 0,lastSeenAt:typeof E.lastSeenAt==="string"?E.lastSeenAt:new Date().toISOString(),referrer:typeof E.referrer==="string"?E.referrer:void 0}}catch{return null}}function T(){let _=J();return t(_?.getItem(k)??null)}function e(_){let E=J();if(!E)return;E.setItem(k,JSON.stringify(_))}function O(){let _=L();if(!_)return;return`${_.location.pathname}${_.location.search}`}function I(){return L()?.location.href}function Q(){return L()?.location.origin??"https://behindthescenes.com"}function c(){return $()?.referrer||void 0}function x(_){let E=$();if(!E?.cookie)return;let S=`${_}=`,N=E.cookie.split(";").map((D)=>D.trim()).find((D)=>D.startsWith(S));if(!N)return;let V=N.slice(S.length);if(V==="")return;try{return decodeURIComponent(V)}catch{return V}}function __(){let _=x("_ga");if(!_)return;let E=_.split(".");if(E.length>=4&&/^GA\d+$/i.test(E[0]??""))return E.slice(-2).join(".");return W(_)}function E_(){let E=$()?.querySelector?.('script[src*="googletagmanager.com/gtag/js?id="]'),S=E&&"src"in E&&typeof E.src==="string"?E.src:void 0;if(!S)return;try{return W(new URL(S).searchParams.get("id"))}catch{return}}function S_(){let _=L(),E=__(),S=E_(),N=typeof _?.gtag==="function"||!!S||!!E;if(!N&&!E&&!S)return;return{tagInstalled:N,clientId:E,measurementId:S}}function w(_){if(!_.googleAnalytics||typeof _.googleAnalytics==="boolean")return;return W(_.googleAnalytics.measurementId)}function N_(_){if(!_.googleAnalytics)return!1;if(_.googleAnalytics===!0)return!1;return _.googleAnalytics.loadTag!==!1&&!!w(_)}function V_(_){let E=L(),S=$();if(!E||!S)return;E.dataLayer=Array.isArray(E.dataLayer)?E.dataLayer:[],E.gtag??=(...V)=>{E.dataLayer?.push(V)};let N=`script[src*="googletagmanager.com/gtag/js?id=${_}"]`;if(!S.querySelector?.(N)){let V=S.createElement("script");V.async=!0,V.src=`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(_)}`,V.setAttribute("data-bts-ga-proxy","true"),(S.head??S.documentElement).appendChild(V)}E.gtag("js",new Date),E.gtag("config",_,{send_page_view:!1})}function D_(){let _=L();if(!_)return;let E=typeof Intl<"u"?W(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,S=_.navigator,N=W(S?.language),V=Array.isArray(S?.languages)?S.languages.filter((X)=>typeof X==="string").slice(0,10):[],D=W(S?.userAgent),B={timezone:E,language:N,languages:V.length>0?V:void 0,userAgent:D};return Object.values(B).some((X)=>X!==void 0)?B:void 0}function z(_){let E=C(),S=S_();return{event_id:_,ga_client_id:typeof S?.clientId==="string"?S.clientId:void 0,ga_tag_installed:typeof S?.tagInstalled==="boolean"?S.tagInstalled:void 0,attribution:E?{utm:E.utm,clickIds:E.clickIds,...E.clickIds,landingUrl:E.landingUrl,referrer:E.referrer}:void 0,client:D_(),googleAnalytics:S,page:{title:$()?.title,url:I()}}}function C(){let _=L();if(!_)return T();let E=new URLSearchParams(_.location.search),S={utm:{},clickIds:{},landingUrl:_.location.href,lastSeenAt:new Date().toISOString(),referrer:c()};for(let D of n){let B=E.get(D);if(!B)continue;if(D.startsWith("utm_")){S.utm[D]=B;continue}S.clickIds[D]=B}for(let D of i){let B=x(D);if(B)S.clickIds[D.replace(/^_/,"")]=B}let N=T(),V={utm:{...N?.utm??{},...S.utm},clickIds:{...N?.clickIds??{},...S.clickIds},landingUrl:S.landingUrl??N?.landingUrl,lastSeenAt:S.lastSeenAt,referrer:S.referrer??N?.referrer};return e(V),V}function W(_){let E=_?.trim();return E?E.slice(0,512):void 0}function W_(_){if(!_)return{};if(_ instanceof Headers){let E={};return _.forEach((S,N)=>{E[N]=S}),E}if(Array.isArray(_))return Object.fromEntries(_.map(([E,S])=>[E,String(S)]));return Object.fromEntries(Object.entries(_).map(([E,S])=>[E,String(S)]))}function B_(_){let E={...r};if(_.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(_.autoTrack&&typeof _.autoTrack==="object")return{pageviews:_.autoTrack.pageviews??E.pageviews,history:_.autoTrack.pageviews===!1?!1:_.autoTrack.history??E.history,outboundLinks:_.autoTrack.outboundLinks??E.outboundLinks,buttonClicks:_.autoTrack.buttonClicks??E.buttonClicks,formSubmissions:_.autoTrack.formSubmissions??E.formSubmissions,search:_.autoTrack.search??E.search,viewContent:_.autoTrack.viewContent??E.viewContent};if(_.autoPageviews===!1)return{...E,pageviews:!1,history:!1};if(_.autoPageviews===!0)return{...E,pageviews:!0};return E}class F{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;viewContentMutationObserver=null;viewContentRescanPending=!1;viewContentListenersTornDown=!1;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(_){if(this.siteKey=_.siteKey,this.endpoint=(_.endpoint??a).replace(/\/$/,""),this.debug=_.debug??!1,this.flushIntervalMs=_.flushIntervalMs??o,this.autoTrack=B_(_),this.requestHeaders=_.requestHeaders,N_(_)){let E=w(_);if(E)V_(E)}if(C(),L())this.installAutoTracking()}static init(_){return new F(_)}getVisitorId(){let _=J(),E=_?.getItem(K);if(E)return E;let S=Z();return _?.setItem(K,S),S}getSessionId(){let _=J(),E=_?.getItem(U);if(E)return E;let S=Z();return _?.setItem(U,S),S}getPersistedJourneyId(){return J()?.getItem(H)??null}log(..._){if(this.debug)console.log("[@bts/analytics]",..._)}installAutoTracking(){let _=L(),E=$();if(!_||!E)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(_),this.popstateHandler=()=>{this.recordPageView(),this.rescanViewContentAfterNavigation()},_.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(S)=>{this.handleAutoClick(S)},this.submitHandler=(S)=>{this.handleAutoSubmit(S)},this.pagehideHandler=()=>{this.viewContentListenersTornDown=!0,this.flushWithBeacon(),this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)E.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)E.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(E);_.addEventListener("pagehide",this.pagehideHandler)}getOrCreateViewContentIntersectionObserver(){if(typeof IntersectionObserver>"u"||this.viewContentListenersTornDown)return null;if(this.viewContentObserver)return this.viewContentObserver;return this.viewContentObserver=new IntersectionObserver((_,E)=>{for(let S of _){if(!S.isIntersecting||!(S.target instanceof Element))continue;this.trackViewContentElement(S.target),E.unobserve(S.target)}}),this.viewContentObserver}observeViewContentElements(_){if(this.viewContentListenersTornDown||this.destroyed)return;let E=this.getOrCreateViewContentIntersectionObserver();if(!E)return;for(let S of Array.from(_.querySelectorAll("[data-bts-view-content]"))){if(this.observedViewContentElements.has(S))continue;this.observedViewContentElements.add(S),E.observe(S)}}scheduleViewContentRescanFromMutations(){if(this.viewContentListenersTornDown)return;if(this.viewContentRescanPending)return;this.viewContentRescanPending=!0,queueMicrotask(()=>{if(this.viewContentRescanPending=!1,this.viewContentListenersTornDown||this.destroyed)return;let _=$();if(_)this.observeViewContentElements(_)})}rescanViewContentAfterNavigation(){if(!this.autoTrack.viewContent)return;if(this.viewContentListenersTornDown)return;let _=$();if(_)this.observeViewContentElements(_)}installViewContentTracking(_){if(typeof IntersectionObserver>"u")return;if(this.observeViewContentElements(_),typeof MutationObserver>"u")return;let E=_.body??_.documentElement;if(!E)return;this.viewContentMutationObserver=new MutationObserver(()=>{this.scheduleViewContentRescanFromMutations()}),this.viewContentMutationObserver.observe(E,{childList:!0,subtree:!0})}trackViewContentElement(_){if(this.trackedViewContentElements.has(_))return;this.trackedViewContentElements.add(_);let E=W(_.getAttribute("data-bts-view-content")||_.getAttribute("data-bts-content-id")||_.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:E,contentTitle:W(_.getAttribute("data-bts-content-title")||_.textContent),contentType:W(_.getAttribute("data-bts-content-type")),elementId:W(_.id)})}patchHistory(_){let E=_.history,S=E.pushState.bind(E),N=E.replaceState.bind(E),V=()=>{C(),this.recordPageView(),this.rescanViewContentAfterNavigation()};return E.pushState=(...D)=>{S(...D),V()},E.replaceState=(...D)=>{N(...D),V()},()=>{E.pushState=S,E.replaceState=N}}handleAutoClick(_){if(_.defaultPrevented)return;let E=_.target;if(!(E instanceof Element))return;if(this.autoTrack.outboundLinks){let N=E.closest("a[href]");if(N instanceof HTMLAnchorElement){let V=W(N.href);if(!V)return;let D=new URL(V,Q());if(D.origin!==Q()){this.trackStandard("outbound_click",{elementHref:V,elementId:W(N.id),elementText:W(N.textContent),linkHost:D.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let S=E.closest("button,[role='button'],[data-bts-track-click]");if(!(S instanceof Element))return;this.trackStandard("button_click",{elementId:W(S.id),elementName:W(S.getAttribute("name")),elementRole:W(S.getAttribute("role")),elementText:W(S.textContent)})}handleAutoSubmit(_){if(_.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let E=_.target;if(!(E instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(E);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:W(E.action),formId:W(E.id),formMethod:W(E.method),formName:W(E.getAttribute("name"))})}trackSearchForm(_){if((_.method||"get").toLowerCase()!=="get")return;let S=this.extractSearchQuery(_);if(!S)return;this.trackStandard("search",{autoCaptured:!0,formAction:W(_.action),formId:W(_.id),formName:W(_.getAttribute("name")),queryKey:S.key,searchQuery:S.value})}extractSearchQuery(_){try{let S=new FormData(_);for(let N of P){let V=S.get(N);if(typeof V==="string"){let D=W(V);if(D)return{key:N,value:D}}}}catch{}let E=_.elements;for(let S of P){let N=E?.namedItem?.(S),V=N&&"value"in N?N.value:void 0;if(typeof V==="string"){let D=W(V);if(D)return{key:S,value:D}}}return null}buildEvent(_,E,S,N){let V=N?.path??O(),D=c(),B=Z(),X=z(B);return{eventName:_,eventType:E,path:V,referrer:D,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...S??{},...X,event_id:S?.event_id??S?.eventId??B}}}queueEvent(_,E,S){let N=Y(_),V=f(N.canonicalEventName,S),D={...E??{}};if(N.aliasOf)D.originalEventName=_;if(this.queue.push(this.buildEvent(N.eventName,V,D)),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 S=`${this.endpoint}${_}`;this.log("POST",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N})}async postWebsiteJson(_,E){let S=this.endpoint.replace(/\/analytics$/,""),N=`${S}${_}`;this.log("POST",N,E);let V=JSON.stringify(E),D=await this.resolveRequestHeaders(_,N,E,V,S);return fetch(N,{method:"POST",headers:D,body:V})}async postJsonKeepalive(_,E){let S=`${this.endpoint}${_}`;this.log("POST keepalive",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N,keepalive:!0})}async resolveRequestHeaders(_,E,S,N,V=this.endpoint){let D={"Content-Type":"application/json"};if(!this.requestHeaders)return D;let B=typeof this.requestHeaders==="function"?await this.requestHeaders({body:S,bodyText:N,endpoint:V,headers:{...D},path:_,siteKey:this.siteKey,url:E}):this.requestHeaders;return{...D,...W_(B)}}async flushBatch(_,E=!1){try{let S=E?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:_}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:_});if(!S.ok)return this.log("flush failed",S.status),!1;return!0}catch(S){return this.log("flush error",S),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let B=[...this.queue];this.queue=[],this.flushBatch(B,!0).then((X)=>{if(!X&&!this.destroyed)this.queue.unshift(...B),this.scheduleFlush()});return}let _=L(),E=_?.navigator?.sendBeacon?.bind(_.navigator);if(!E)return;let S=[...this.queue];this.queue=[];let N=`${this.endpoint}/ingest/batch`,V=JSON.stringify({siteKey:this.siteKey,events:S}),D=new Blob([V],{type:"application/json"});E(N,D)}async flushNow(){if(this.queue.length===0)return;let _=[...this.queue];if(this.queue=[],!await this.flushBatch(_)&&!this.destroyed)this.queue.unshift(..._),this.scheduleFlush()}recordPageView(_){let E=_??O(),S=I()??E??"/";if(S===this.lastTrackedUrl)return;this.lastTrackedUrl=S,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:E})),this.scheduleFlush()}page(_){this.lastTrackedUrl=null,this.recordPageView(_)}track(_,E){this.queueEvent(_,E)}trackStandard(_,E){this.queueEvent(_,E)}identify(_,E){this.queue.push(this.buildEvent("identify","identify",{traits:E??{},userId:_})),this.scheduleFlush()}listStandardEvents(){return G()}async submitContactForm(_){let E=Z(),S=z(E),N=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:_.locationId,email:_.email,phone:_.phone,subject:_.subject,body:_.body,name:_.name,firstName:_.firstName,lastName:_.lastName,source:_.source,tags:_.tags,customFields:_.customFields,metadata:{..._.metadata??{},...S}});if(!N.ok)throw Error(`contact form submit failed: ${N.status}`);return await N.json()}async startFunnel(_){let E=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:_??O()});if(!E.ok)throw Error(`journey/start failed: ${E.status}`);let S=await E.json();return J()?.setItem(R,S.journeyToken),J()?.setItem(H,S.journeyId),S}getPersistedJourneyToken(){return J()?.getItem(R)??null}decorateUrl(_,E){let S=E??this.getPersistedJourneyToken(),N=new URL(_,Q());if(N.searchParams.set(d,this.siteKey),S)N.searchParams.set(s,S);return N.toString()}async notifyHandoff(_,E){let S=await this.postJson("/journey/handoff",{journeyToken:_,context:E});if(!S.ok)throw Error(`journey/handoff failed: ${S.status}`)}destroy(){this.destroyed=!0,this.viewContentListenersTornDown=!0;let _=L(),E=$();if(E&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))E.removeEventListener("click",this.clickHandler,!0);if(E&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))E.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null,_&&this.popstateHandler)_.removeEventListener("popstate",this.popstateHandler);if(_&&this.pagehideHandler)_.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function L_(_){return F.init(_)}
|
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1 @@
|
|
|
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
|
|
1
|
+
var Q=["page_view","view_content","search","lead","sign_up","begin_checkout","add_payment_info","purchase","outbound_click","button_click","form_submit"],x=new Set(Q),w=new Set(["lead","sign_up","begin_checkout","add_payment_info","purchase"]),g={$pageview:"page_view",checkout_started:"begin_checkout",lead_capture:"lead",purchase_completed:"purchase",registration_complete:"sign_up"};function v(_){return x.has(_)}function C(_){let E=_.trim();if(E.length===0)return{eventName:E,canonicalEventName:E,isStandard:!1};let S=E.toLowerCase(),N=g[S];if(N)return{eventName:N,canonicalEventName:N,aliasOf:N,isStandard:!0};return{eventName:E,canonicalEventName:E,isStandard:v(E)}}function j(_,E){if(E)return E;if(_==="page_view")return"page_view";if(_==="identify")return"identify";if(w.has(_))return"conversion";return"custom"}function A(){return[...Q]}var Y="bts_analytics_vid",f="bts_analytics_sid",K="bts_analytics_jt",U="bts_analytics_jid",T="bts_analytics_attr",h="bts_site",b="bts_jt",m=300,y="https://api.bts.dev/v2/website/analytics",u={pageviews:!0,history:!0,outboundLinks:!0,buttonClicks:!0,formSubmissions:!0,search:!0,viewContent:!0},R=["q","query","search"],l=["utm_source","utm_medium","utm_campaign","utm_term","utm_content","fbclid","gclid","gbraid","li_fat_id","msclkid","ttclid","wbraid"],p=["_fbp","_fbc"];function L(){return typeof window>"u"?null:window}function $(){return typeof document>"u"?null:document}function J(){return L()?.localStorage??null}function Z(){if(typeof crypto<"u"&&typeof crypto.randomUUID==="function")return crypto.randomUUID();return`v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`}function F(_){return _!==null&&typeof _==="object"&&!Array.isArray(_)}function d(_){if(!_)return null;try{let E=JSON.parse(_);if(!F(E))return null;let S=F(E.utm)?Object.fromEntries(Object.entries(E.utm).filter((V)=>typeof V[1]==="string")):{},N=F(E.clickIds)?Object.fromEntries(Object.entries(E.clickIds).filter((V)=>typeof V[1]==="string")):{};return{utm:S,clickIds:N,landingUrl:typeof E.landingUrl==="string"?E.landingUrl:void 0,lastSeenAt:typeof E.lastSeenAt==="string"?E.lastSeenAt:new Date().toISOString(),referrer:typeof E.referrer==="string"?E.referrer:void 0}}catch{return null}}function H(){let _=J();return d(_?.getItem(T)??null)}function s(_){let E=J();if(!E)return;E.setItem(T,JSON.stringify(_))}function q(){let _=L();if(!_)return;return`${_.location.pathname}${_.location.search}`}function z(){return L()?.location.href}function G(){return L()?.location.origin??"https://behindthescenes.com"}function k(){return $()?.referrer||void 0}function I(_){let E=$();if(!E?.cookie)return;let S=`${_}=`,N=E.cookie.split(";").map((D)=>D.trim()).find((D)=>D.startsWith(S));if(!N)return;let V=N.slice(S.length);if(V==="")return;try{return decodeURIComponent(V)}catch{return V}}function o(){let _=I("_ga");if(!_)return;let E=_.split(".");if(E.length>=4&&/^GA\d+$/i.test(E[0]??""))return E.slice(-2).join(".");return W(_)}function a(){let E=$()?.querySelector?.('script[src*="googletagmanager.com/gtag/js?id="]'),S=E&&"src"in E&&typeof E.src==="string"?E.src:void 0;if(!S)return;try{return W(new URL(S).searchParams.get("id"))}catch{return}}function r(){let _=L(),E=o(),S=a(),N=typeof _?.gtag==="function"||!!S||!!E;if(!N&&!E&&!S)return;return{tagInstalled:N,clientId:E,measurementId:S}}function c(_){if(!_.googleAnalytics||typeof _.googleAnalytics==="boolean")return;return W(_.googleAnalytics.measurementId)}function n(_){if(!_.googleAnalytics)return!1;if(_.googleAnalytics===!0)return!1;return _.googleAnalytics.loadTag!==!1&&!!c(_)}function i(_){let E=L(),S=$();if(!E||!S)return;E.dataLayer=Array.isArray(E.dataLayer)?E.dataLayer:[],E.gtag??=(...V)=>{E.dataLayer?.push(V)};let N=`script[src*="googletagmanager.com/gtag/js?id=${_}"]`;if(!S.querySelector?.(N)){let V=S.createElement("script");V.async=!0,V.src=`https://www.googletagmanager.com/gtag/js?id=${encodeURIComponent(_)}`,V.setAttribute("data-bts-ga-proxy","true"),(S.head??S.documentElement).appendChild(V)}E.gtag("js",new Date),E.gtag("config",_,{send_page_view:!1})}function t(){let _=L();if(!_)return;let E=typeof Intl<"u"?W(Intl.DateTimeFormat().resolvedOptions().timeZone):void 0,S=_.navigator,N=W(S?.language),V=Array.isArray(S?.languages)?S.languages.filter((X)=>typeof X==="string").slice(0,10):[],D=W(S?.userAgent),B={timezone:E,language:N,languages:V.length>0?V:void 0,userAgent:D};return Object.values(B).some((X)=>X!==void 0)?B:void 0}function P(_){let E=M(),S=r();return{event_id:_,ga_client_id:typeof S?.clientId==="string"?S.clientId:void 0,ga_tag_installed:typeof S?.tagInstalled==="boolean"?S.tagInstalled:void 0,attribution:E?{utm:E.utm,clickIds:E.clickIds,...E.clickIds,landingUrl:E.landingUrl,referrer:E.referrer}:void 0,client:t(),googleAnalytics:S,page:{title:$()?.title,url:z()}}}function M(){let _=L();if(!_)return H();let E=new URLSearchParams(_.location.search),S={utm:{},clickIds:{},landingUrl:_.location.href,lastSeenAt:new Date().toISOString(),referrer:k()};for(let D of l){let B=E.get(D);if(!B)continue;if(D.startsWith("utm_")){S.utm[D]=B;continue}S.clickIds[D]=B}for(let D of p){let B=I(D);if(B)S.clickIds[D.replace(/^_/,"")]=B}let N=H(),V={utm:{...N?.utm??{},...S.utm},clickIds:{...N?.clickIds??{},...S.clickIds},landingUrl:S.landingUrl??N?.landingUrl,lastSeenAt:S.lastSeenAt,referrer:S.referrer??N?.referrer};return s(V),V}function W(_){let E=_?.trim();return E?E.slice(0,512):void 0}function e(_){if(!_)return{};if(_ instanceof Headers){let E={};return _.forEach((S,N)=>{E[N]=S}),E}if(Array.isArray(_))return Object.fromEntries(_.map(([E,S])=>[E,String(S)]));return Object.fromEntries(Object.entries(_).map(([E,S])=>[E,String(S)]))}function __(_){let E={...u};if(_.autoTrack===!1)return{pageviews:!1,history:!1,outboundLinks:!1,buttonClicks:!1,formSubmissions:!1,search:!1,viewContent:!1};if(_.autoTrack&&typeof _.autoTrack==="object")return{pageviews:_.autoTrack.pageviews??E.pageviews,history:_.autoTrack.pageviews===!1?!1:_.autoTrack.history??E.history,outboundLinks:_.autoTrack.outboundLinks??E.outboundLinks,buttonClicks:_.autoTrack.buttonClicks??E.buttonClicks,formSubmissions:_.autoTrack.formSubmissions??E.formSubmissions,search:_.autoTrack.search??E.search,viewContent:_.autoTrack.viewContent??E.viewContent};if(_.autoPageviews===!1)return{...E,pageviews:!1,history:!1};if(_.autoPageviews===!0)return{...E,pageviews:!0};return E}class O{siteKey;endpoint;debug;flushIntervalMs;autoTrack;requestHeaders;queue=[];flushTimer=null;destroyed=!1;lastTrackedUrl=null;unpatchHistory=null;clickHandler=null;submitHandler=null;pagehideHandler=null;popstateHandler=null;viewContentObserver=null;viewContentMutationObserver=null;viewContentRescanPending=!1;viewContentListenersTornDown=!1;observedViewContentElements=new WeakSet;trackedViewContentElements=new WeakSet;constructor(_){if(this.siteKey=_.siteKey,this.endpoint=(_.endpoint??y).replace(/\/$/,""),this.debug=_.debug??!1,this.flushIntervalMs=_.flushIntervalMs??m,this.autoTrack=__(_),this.requestHeaders=_.requestHeaders,n(_)){let E=c(_);if(E)i(E)}if(M(),L())this.installAutoTracking()}static init(_){return new O(_)}getVisitorId(){let _=J(),E=_?.getItem(Y);if(E)return E;let S=Z();return _?.setItem(Y,S),S}getSessionId(){let _=J(),E=_?.getItem(f);if(E)return E;let S=Z();return _?.setItem(f,S),S}getPersistedJourneyId(){return J()?.getItem(U)??null}log(..._){if(this.debug)console.log("[@bts/analytics]",..._)}installAutoTracking(){let _=L(),E=$();if(!_||!E)return;if(this.autoTrack.pageviews)this.recordPageView();if(this.autoTrack.history)this.unpatchHistory=this.patchHistory(_),this.popstateHandler=()=>{this.recordPageView(),this.rescanViewContentAfterNavigation()},_.addEventListener("popstate",this.popstateHandler);if(this.clickHandler=(S)=>{this.handleAutoClick(S)},this.submitHandler=(S)=>{this.handleAutoSubmit(S)},this.pagehideHandler=()=>{this.viewContentListenersTornDown=!0,this.flushWithBeacon(),this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null},this.autoTrack.outboundLinks||this.autoTrack.buttonClicks)E.addEventListener("click",this.clickHandler,!0);if(this.autoTrack.formSubmissions||this.autoTrack.search)E.addEventListener("submit",this.submitHandler,!0);if(this.autoTrack.viewContent)this.installViewContentTracking(E);_.addEventListener("pagehide",this.pagehideHandler)}getOrCreateViewContentIntersectionObserver(){if(typeof IntersectionObserver>"u"||this.viewContentListenersTornDown)return null;if(this.viewContentObserver)return this.viewContentObserver;return this.viewContentObserver=new IntersectionObserver((_,E)=>{for(let S of _){if(!S.isIntersecting||!(S.target instanceof Element))continue;this.trackViewContentElement(S.target),E.unobserve(S.target)}}),this.viewContentObserver}observeViewContentElements(_){if(this.viewContentListenersTornDown||this.destroyed)return;let E=this.getOrCreateViewContentIntersectionObserver();if(!E)return;for(let S of Array.from(_.querySelectorAll("[data-bts-view-content]"))){if(this.observedViewContentElements.has(S))continue;this.observedViewContentElements.add(S),E.observe(S)}}scheduleViewContentRescanFromMutations(){if(this.viewContentListenersTornDown)return;if(this.viewContentRescanPending)return;this.viewContentRescanPending=!0,queueMicrotask(()=>{if(this.viewContentRescanPending=!1,this.viewContentListenersTornDown||this.destroyed)return;let _=$();if(_)this.observeViewContentElements(_)})}rescanViewContentAfterNavigation(){if(!this.autoTrack.viewContent)return;if(this.viewContentListenersTornDown)return;let _=$();if(_)this.observeViewContentElements(_)}installViewContentTracking(_){if(typeof IntersectionObserver>"u")return;if(this.observeViewContentElements(_),typeof MutationObserver>"u")return;let E=_.body??_.documentElement;if(!E)return;this.viewContentMutationObserver=new MutationObserver(()=>{this.scheduleViewContentRescanFromMutations()}),this.viewContentMutationObserver.observe(E,{childList:!0,subtree:!0})}trackViewContentElement(_){if(this.trackedViewContentElements.has(_))return;this.trackedViewContentElements.add(_);let E=W(_.getAttribute("data-bts-view-content")||_.getAttribute("data-bts-content-id")||_.id);this.trackStandard("view_content",{autoCaptured:!0,contentId:E,contentTitle:W(_.getAttribute("data-bts-content-title")||_.textContent),contentType:W(_.getAttribute("data-bts-content-type")),elementId:W(_.id)})}patchHistory(_){let E=_.history,S=E.pushState.bind(E),N=E.replaceState.bind(E),V=()=>{M(),this.recordPageView(),this.rescanViewContentAfterNavigation()};return E.pushState=(...D)=>{S(...D),V()},E.replaceState=(...D)=>{N(...D),V()},()=>{E.pushState=S,E.replaceState=N}}handleAutoClick(_){if(_.defaultPrevented)return;let E=_.target;if(!(E instanceof Element))return;if(this.autoTrack.outboundLinks){let N=E.closest("a[href]");if(N instanceof HTMLAnchorElement){let V=W(N.href);if(!V)return;let D=new URL(V,G());if(D.origin!==G()){this.trackStandard("outbound_click",{elementHref:V,elementId:W(N.id),elementText:W(N.textContent),linkHost:D.hostname});return}}}if(!this.autoTrack.buttonClicks)return;let S=E.closest("button,[role='button'],[data-bts-track-click]");if(!(S instanceof Element))return;this.trackStandard("button_click",{elementId:W(S.id),elementName:W(S.getAttribute("name")),elementRole:W(S.getAttribute("role")),elementText:W(S.textContent)})}handleAutoSubmit(_){if(_.defaultPrevented||!this.autoTrack.formSubmissions&&!this.autoTrack.search)return;let E=_.target;if(!(E instanceof HTMLFormElement))return;if(this.autoTrack.search)this.trackSearchForm(E);if(!this.autoTrack.formSubmissions)return;this.trackStandard("form_submit",{formAction:W(E.action),formId:W(E.id),formMethod:W(E.method),formName:W(E.getAttribute("name"))})}trackSearchForm(_){if((_.method||"get").toLowerCase()!=="get")return;let S=this.extractSearchQuery(_);if(!S)return;this.trackStandard("search",{autoCaptured:!0,formAction:W(_.action),formId:W(_.id),formName:W(_.getAttribute("name")),queryKey:S.key,searchQuery:S.value})}extractSearchQuery(_){try{let S=new FormData(_);for(let N of R){let V=S.get(N);if(typeof V==="string"){let D=W(V);if(D)return{key:N,value:D}}}}catch{}let E=_.elements;for(let S of R){let N=E?.namedItem?.(S),V=N&&"value"in N?N.value:void 0;if(typeof V==="string"){let D=W(V);if(D)return{key:S,value:D}}}return null}buildEvent(_,E,S,N){let V=N?.path??q(),D=k(),B=Z(),X=P(B);return{eventName:_,eventType:E,path:V,referrer:D,occurredAt:new Date().toISOString(),journeyId:this.getPersistedJourneyId()??void 0,visitorId:this.getVisitorId(),sessionId:this.getSessionId(),properties:{...S??{},...X,event_id:S?.event_id??S?.eventId??B}}}queueEvent(_,E,S){let N=C(_),V=j(N.canonicalEventName,S),D={...E??{}};if(N.aliasOf)D.originalEventName=_;if(this.queue.push(this.buildEvent(N.eventName,V,D)),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 S=`${this.endpoint}${_}`;this.log("POST",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N})}async postWebsiteJson(_,E){let S=this.endpoint.replace(/\/analytics$/,""),N=`${S}${_}`;this.log("POST",N,E);let V=JSON.stringify(E),D=await this.resolveRequestHeaders(_,N,E,V,S);return fetch(N,{method:"POST",headers:D,body:V})}async postJsonKeepalive(_,E){let S=`${this.endpoint}${_}`;this.log("POST keepalive",S,E);let N=JSON.stringify(E),V=await this.resolveRequestHeaders(_,S,E,N,this.endpoint);return fetch(S,{method:"POST",headers:V,body:N,keepalive:!0})}async resolveRequestHeaders(_,E,S,N,V=this.endpoint){let D={"Content-Type":"application/json"};if(!this.requestHeaders)return D;let B=typeof this.requestHeaders==="function"?await this.requestHeaders({body:S,bodyText:N,endpoint:V,headers:{...D},path:_,siteKey:this.siteKey,url:E}):this.requestHeaders;return{...D,...e(B)}}async flushBatch(_,E=!1){try{let S=E?await this.postJsonKeepalive("/ingest/batch",{siteKey:this.siteKey,events:_}):await this.postJson("/ingest/batch",{siteKey:this.siteKey,events:_});if(!S.ok)return this.log("flush failed",S.status),!1;return!0}catch(S){return this.log("flush error",S),!1}}flushWithBeacon(){if(this.queue.length===0)return;if(this.requestHeaders){let B=[...this.queue];this.queue=[],this.flushBatch(B,!0).then((X)=>{if(!X&&!this.destroyed)this.queue.unshift(...B),this.scheduleFlush()});return}let _=L(),E=_?.navigator?.sendBeacon?.bind(_.navigator);if(!E)return;let S=[...this.queue];this.queue=[];let N=`${this.endpoint}/ingest/batch`,V=JSON.stringify({siteKey:this.siteKey,events:S}),D=new Blob([V],{type:"application/json"});E(N,D)}async flushNow(){if(this.queue.length===0)return;let _=[...this.queue];if(this.queue=[],!await this.flushBatch(_)&&!this.destroyed)this.queue.unshift(..._),this.scheduleFlush()}recordPageView(_){let E=_??q(),S=z()??E??"/";if(S===this.lastTrackedUrl)return;this.lastTrackedUrl=S,this.queue.push(this.buildEvent("page_view","page_view",{autoCaptured:!0},{path:E})),this.scheduleFlush()}page(_){this.lastTrackedUrl=null,this.recordPageView(_)}track(_,E){this.queueEvent(_,E)}trackStandard(_,E){this.queueEvent(_,E)}identify(_,E){this.queue.push(this.buildEvent("identify","identify",{traits:E??{},userId:_})),this.scheduleFlush()}listStandardEvents(){return A()}async submitContactForm(_){let E=Z(),S=P(E),N=await this.postWebsiteJson("/ghl/leads",{siteKey:this.siteKey,locationId:_.locationId,email:_.email,phone:_.phone,subject:_.subject,body:_.body,name:_.name,firstName:_.firstName,lastName:_.lastName,source:_.source,tags:_.tags,customFields:_.customFields,metadata:{..._.metadata??{},...S}});if(!N.ok)throw Error(`contact form submit failed: ${N.status}`);return await N.json()}async startFunnel(_){let E=await this.postJson("/journey/start",{siteKey:this.siteKey,entryPath:_??q()});if(!E.ok)throw Error(`journey/start failed: ${E.status}`);let S=await E.json();return J()?.setItem(K,S.journeyToken),J()?.setItem(U,S.journeyId),S}getPersistedJourneyToken(){return J()?.getItem(K)??null}decorateUrl(_,E){let S=E??this.getPersistedJourneyToken(),N=new URL(_,G());if(N.searchParams.set(h,this.siteKey),S)N.searchParams.set(b,S);return N.toString()}async notifyHandoff(_,E){let S=await this.postJson("/journey/handoff",{journeyToken:_,context:E});if(!S.ok)throw Error(`journey/handoff failed: ${S.status}`)}destroy(){this.destroyed=!0,this.viewContentListenersTornDown=!0;let _=L(),E=$();if(E&&this.clickHandler&&(this.autoTrack.outboundLinks||this.autoTrack.buttonClicks))E.removeEventListener("click",this.clickHandler,!0);if(E&&this.submitHandler&&(this.autoTrack.formSubmissions||this.autoTrack.search))E.removeEventListener("submit",this.submitHandler,!0);if(this.viewContentObserver?.disconnect(),this.viewContentObserver=null,this.viewContentMutationObserver?.disconnect(),this.viewContentMutationObserver=null,_&&this.popstateHandler)_.removeEventListener("popstate",this.popstateHandler);if(_&&this.pagehideHandler)_.removeEventListener("pagehide",this.pagehideHandler);if(this.flushTimer)clearTimeout(this.flushTimer),this.flushTimer=null;this.unpatchHistory?.(),this.unpatchHistory=null,this.flushWithBeacon()}}function N_(_){return O.init(_)}export{A as listStandardWebEvents,N_ as createBTSAnalytics,O as BTSAnalytics};
|
package/dist/manifest.json
CHANGED
package/dist/types/browser.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BTSAnalytics, createBTSAnalytics, listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsInit, type BTSAnalyticsStandardEventName } from "./index";
|
|
1
|
+
import { BTSAnalytics, createBTSAnalytics, listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsContactFormInput, type BTSAnalyticsContactFormResult, type BTSAnalyticsInit, type BTSAnalyticsRequestContext, type BTSAnalyticsRequestHeaders, type BTSAnalyticsStandardEventName } from "./index";
|
|
2
2
|
declare global {
|
|
3
3
|
interface Window {
|
|
4
4
|
BTSAnalytics?: {
|
|
@@ -7,6 +7,9 @@ declare global {
|
|
|
7
7
|
listStandardWebEvents: typeof listStandardWebEvents;
|
|
8
8
|
};
|
|
9
9
|
createBTSAnalytics?: typeof createBTSAnalytics;
|
|
10
|
+
btsAnalytics?: BTSAnalytics;
|
|
11
|
+
btsDataLayer?: Array<ArrayLike<unknown>>;
|
|
12
|
+
bts?: (...args: unknown[]) => void;
|
|
10
13
|
}
|
|
11
14
|
}
|
|
12
|
-
export { BTSAnalytics, createBTSAnalytics, listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsInit, type BTSAnalyticsStandardEventName, };
|
|
15
|
+
export { BTSAnalytics, createBTSAnalytics, listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsContactFormInput, type BTSAnalyticsContactFormResult, type BTSAnalyticsInit, type BTSAnalyticsRequestContext, type BTSAnalyticsRequestHeaders, type BTSAnalyticsStandardEventName, };
|
package/dist/types/index.d.ts
CHANGED
|
@@ -5,43 +5,112 @@ type AutoTrackConfig = {
|
|
|
5
5
|
outboundLinks: boolean;
|
|
6
6
|
buttonClicks: boolean;
|
|
7
7
|
formSubmissions: boolean;
|
|
8
|
+
search: boolean;
|
|
9
|
+
viewContent: boolean;
|
|
8
10
|
};
|
|
11
|
+
export type BTSAnalyticsRequestContext = {
|
|
12
|
+
body: unknown;
|
|
13
|
+
bodyText: string;
|
|
14
|
+
/** Base URL for the outgoing request (e.g. analytics `.../website/analytics` vs website `.../website`). */
|
|
15
|
+
endpoint: string;
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
path: string;
|
|
18
|
+
siteKey: string;
|
|
19
|
+
url: string;
|
|
20
|
+
};
|
|
21
|
+
export type BTSAnalyticsRequestHeaders = HeadersInit | ((request: BTSAnalyticsRequestContext) => HeadersInit | Promise<HeadersInit>);
|
|
9
22
|
export type BTSAnalyticsInit = {
|
|
10
23
|
siteKey: string;
|
|
11
|
-
endpoint
|
|
24
|
+
/** Defaults to the production BTS website analytics endpoint. Override for staging/dev. */
|
|
25
|
+
endpoint?: string;
|
|
12
26
|
autoPageviews?: boolean;
|
|
13
27
|
autoTrack?: boolean | Partial<AutoTrackConfig>;
|
|
14
28
|
debug?: boolean;
|
|
15
29
|
flushIntervalMs?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Optional GA4 compatibility mode. When a measurement ID is provided, the SDK loads the Google tag with
|
|
32
|
+
* `send_page_view: false` so GA scanners can detect an installed tag while BTS forwards events server-side.
|
|
33
|
+
*/
|
|
34
|
+
googleAnalytics?: boolean | {
|
|
35
|
+
loadTag?: boolean;
|
|
36
|
+
measurementId?: string;
|
|
37
|
+
};
|
|
38
|
+
requestHeaders?: BTSAnalyticsRequestHeaders;
|
|
16
39
|
};
|
|
17
40
|
export type BTSAnalyticsPayloadProperties = Record<string, unknown>;
|
|
41
|
+
/**
|
|
42
|
+
* Contact form payload for BTS website lead capture.
|
|
43
|
+
*
|
|
44
|
+
* The backend requires at least one of `email` or `phone`; validation is enforced server-side.
|
|
45
|
+
*/
|
|
46
|
+
export type BTSAnalyticsContactFormInput = {
|
|
47
|
+
locationId: string;
|
|
48
|
+
email?: string;
|
|
49
|
+
phone?: string;
|
|
50
|
+
subject?: string;
|
|
51
|
+
body?: string;
|
|
52
|
+
name?: string;
|
|
53
|
+
firstName?: string;
|
|
54
|
+
lastName?: string;
|
|
55
|
+
source?: string;
|
|
56
|
+
tags?: string[];
|
|
57
|
+
customFields?: Record<string, string | number | boolean>;
|
|
58
|
+
metadata?: Record<string, unknown>;
|
|
59
|
+
};
|
|
60
|
+
export type BTSAnalyticsContactFormResult = {
|
|
61
|
+
ok: boolean;
|
|
62
|
+
data?: unknown;
|
|
63
|
+
};
|
|
18
64
|
export declare class BTSAnalytics {
|
|
19
65
|
private siteKey;
|
|
20
66
|
private endpoint;
|
|
21
67
|
private debug;
|
|
22
68
|
private flushIntervalMs;
|
|
23
69
|
private autoTrack;
|
|
70
|
+
private requestHeaders?;
|
|
24
71
|
private queue;
|
|
25
72
|
private flushTimer;
|
|
73
|
+
private destroyed;
|
|
26
74
|
private lastTrackedUrl;
|
|
27
75
|
private unpatchHistory;
|
|
28
76
|
private clickHandler;
|
|
29
77
|
private submitHandler;
|
|
30
78
|
private pagehideHandler;
|
|
31
79
|
private popstateHandler;
|
|
80
|
+
private viewContentObserver;
|
|
81
|
+
private viewContentMutationObserver;
|
|
82
|
+
private viewContentRescanPending;
|
|
83
|
+
private viewContentListenersTornDown;
|
|
84
|
+
private observedViewContentElements;
|
|
85
|
+
private trackedViewContentElements;
|
|
32
86
|
constructor(init: BTSAnalyticsInit);
|
|
33
87
|
static init(opts: BTSAnalyticsInit): BTSAnalytics;
|
|
34
88
|
getVisitorId(): string;
|
|
35
89
|
getSessionId(): string;
|
|
90
|
+
private getPersistedJourneyId;
|
|
36
91
|
private log;
|
|
37
92
|
private installAutoTracking;
|
|
93
|
+
private getOrCreateViewContentIntersectionObserver;
|
|
94
|
+
/** Re-query `[data-bts-view-content]` and register any new elements with the intersection observer. */
|
|
95
|
+
private observeViewContentElements;
|
|
96
|
+
private scheduleViewContentRescanFromMutations;
|
|
97
|
+
private rescanViewContentAfterNavigation;
|
|
98
|
+
private installViewContentTracking;
|
|
99
|
+
private trackViewContentElement;
|
|
38
100
|
private patchHistory;
|
|
39
101
|
private handleAutoClick;
|
|
40
102
|
private handleAutoSubmit;
|
|
103
|
+
private trackSearchForm;
|
|
104
|
+
private extractSearchQuery;
|
|
41
105
|
private buildEvent;
|
|
42
106
|
private queueEvent;
|
|
43
107
|
private scheduleFlush;
|
|
44
108
|
private postJson;
|
|
109
|
+
private postWebsiteJson;
|
|
110
|
+
private postJsonKeepalive;
|
|
111
|
+
private resolveRequestHeaders;
|
|
112
|
+
/** @returns true when the batch was accepted (2xx). */
|
|
113
|
+
private flushBatch;
|
|
45
114
|
private flushWithBeacon;
|
|
46
115
|
flushNow(): Promise<void>;
|
|
47
116
|
private recordPageView;
|
|
@@ -50,6 +119,7 @@ export declare class BTSAnalytics {
|
|
|
50
119
|
trackStandard(eventName: BTSAnalyticsStandardEventName, properties?: BTSAnalyticsPayloadProperties): void;
|
|
51
120
|
identify(userId: string, traits?: BTSAnalyticsPayloadProperties): void;
|
|
52
121
|
listStandardEvents(): BTSAnalyticsStandardEventName[];
|
|
122
|
+
submitContactForm(input: BTSAnalyticsContactFormInput): Promise<BTSAnalyticsContactFormResult>;
|
|
53
123
|
/** Start a cross-site funnel; persists journey token for decorateUrl. */
|
|
54
124
|
startFunnel(entryPath?: string): Promise<{
|
|
55
125
|
journeyId: string;
|
|
@@ -64,4 +134,4 @@ export declare class BTSAnalytics {
|
|
|
64
134
|
destroy(): void;
|
|
65
135
|
}
|
|
66
136
|
export declare function createBTSAnalytics(init: BTSAnalyticsInit): BTSAnalytics;
|
|
67
|
-
export { listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsStandardEventName };
|
|
137
|
+
export { listStandardWebEvents, type BTSAnalyticsEventType, type BTSAnalyticsStandardEventName, };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@behindthescenes/analytics",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Browser analytics SDK for BTS external-site event tracking and attribution.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -47,12 +47,13 @@
|
|
|
47
47
|
"scripts": {
|
|
48
48
|
"build": "bun ./scripts/build.ts",
|
|
49
49
|
"build:hosted": "bun ./scripts/sync-hosted-bundle.ts",
|
|
50
|
+
"release:version": "bun ./scripts/bump-release-version.ts",
|
|
50
51
|
"type-check": "tsc --noEmit",
|
|
51
52
|
"test": "bun test",
|
|
52
53
|
"lint": "eslint . --fix",
|
|
53
54
|
"lint:check": "eslint .",
|
|
54
55
|
"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
|
+
"release:publish": "bun run release:version && bun run release:check && npm publish --access public",
|
|
56
57
|
"prepublishOnly": "bun run release:check"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
@@ -61,4 +62,4 @@
|
|
|
61
62
|
"eslint": "^9.17.0",
|
|
62
63
|
"typescript": "^5.8.3"
|
|
63
64
|
}
|
|
64
|
-
}
|
|
65
|
+
}
|
|
@@ -1,12 +0,0 @@
|
|
|
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": "AAAO,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,ECtnB/B,GAAI,OAAO,OAAW,IACpB,OAAO,aAAe,CACpB,eACA,qBACA,uBACF,EACA,OAAO,mBAAqB",
|
|
10
|
-
"debugId": "8CE5844CD6E1239864756E2164756E21",
|
|
11
|
-
"names": []
|
|
12
|
-
}
|
package/dist/cjs/index.js.map
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
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": "164F30813325DE1964756E2164756E21",
|
|
10
|
-
"names": []
|
|
11
|
-
}
|
package/dist/esm/index.js.map
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
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
|
-
}
|