@faststats/web 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,3 +2,70 @@
2
2
 
3
3
  > [!IMPORTANT]
4
4
  > This repository is in early development and not intended for production use.
5
+
6
+ The base SDK only handles analytics, identity, sessions, navigation, and consent.
7
+ Error tracking, Web Vitals, and session replay are independent extensions, so
8
+ applications only download the products they use.
9
+
10
+ ```ts
11
+ import { init, track } from "@faststats/web";
12
+ import { errorTracking } from "@faststats/web/error";
13
+ import { outboundLinks } from "@faststats/web/outbound-links";
14
+ import { sessionReplay } from "@faststats/web/replay";
15
+ import { webVitals } from "@faststats/web/web-vitals";
16
+
17
+ init({
18
+ siteKey: "site_123",
19
+ extensions: [
20
+ outboundLinks(),
21
+ errorTracking(),
22
+ webVitals({ attribution: true }),
23
+ sessionReplay({ recordConsole: true }),
24
+ ],
25
+ });
26
+
27
+ track("checkout_completed", { total: 42 });
28
+ ```
29
+
30
+ `init()` starts one shared client and is idempotent, making it safe to call from
31
+ application bootstrap code. Calls made before initialization are ignored rather
32
+ than buffered with stale context. Use `shutdown()` before initializing with new
33
+ options.
34
+
35
+ For multiple sites, tests, or framework integrations, create an independent
36
+ client with explicit lifecycle control:
37
+
38
+ ```ts
39
+ import { createClient } from "@faststats/web";
40
+
41
+ const analytics = createClient({ siteKey: "site_123" });
42
+ analytics.start();
43
+ analytics.track("checkout_completed", { total: 42 });
44
+ analytics.destroy();
45
+ ```
46
+
47
+ Consent remains client-local: use `"anonymous"` for cookieless collection,
48
+ `"granted"` for normal collection, or `"denied"` to keep the client dormant.
49
+
50
+ ### Custom extensions
51
+
52
+ An extension registers only the lifecycle hooks it needs. Hook failures are
53
+ isolated from analytics collection, asynchronous setup is safe during teardown,
54
+ and cleanup receives `discard: true` when consent is revoked.
55
+
56
+ ```ts
57
+ import type { AnalyticsExtension } from "@faststats/web";
58
+
59
+ export function diagnostics(): AnalyticsExtension {
60
+ return {
61
+ name: "diagnostics",
62
+ setup(context) {
63
+ const off = context.on("pageChange", ({ url }) => {
64
+ console.debug("page changed", url, context.getSession());
65
+ });
66
+
67
+ return () => off();
68
+ },
69
+ };
70
+ }
71
+ ```
@@ -1,6 +1,5 @@
1
1
  //#region src/utils/api-urls.ts
2
2
  const ANALYTICS_BASE = "https://metrics.faststats.dev";
3
- const FEATURE_FLAGS_BASE = "https://flags.faststats.dev";
4
3
  function resolveBaseUrl(input, fallback) {
5
4
  return input?.replace(/\/+$/, "") || fallback;
6
5
  }
@@ -8,8 +7,7 @@ const URLS = {
8
7
  events: "/v1/web",
9
8
  identify: "/v1/identify",
10
9
  replay: "/v1/replay",
11
- vitals: "/v1/vitals",
12
- flags: "/v1/check"
10
+ vitals: "/v1/vitals"
13
11
  };
14
12
  function sanitizeUrl(url, includeHash = false) {
15
13
  try {
@@ -26,12 +24,21 @@ const UTM_KEYS = [
26
24
  "utm_term",
27
25
  "utm_content"
28
26
  ];
27
+ function getReferrer() {
28
+ const raw = document.referrer;
29
+ if (!raw) return null;
30
+ try {
31
+ const pageOrigin = new URL(location.href).origin;
32
+ if (new URL(raw, location.href).origin === pageOrigin) return null;
33
+ } catch {}
34
+ return sanitizeUrl(raw);
35
+ }
29
36
  function getPageContext(includeHash = false) {
30
37
  const hash = includeHash ? location.hash : "";
31
38
  const ctx = {
32
39
  page: `${location.pathname}${hash}`,
33
40
  url: sanitizeUrl(location.href, includeHash),
34
- referrer: document.referrer ? sanitizeUrl(document.referrer) : null,
41
+ referrer: getReferrer(),
35
42
  title: document.title || ""
36
43
  };
37
44
  if (!location.search) return ctx;
@@ -43,4 +50,4 @@ function getPageContext(includeHash = false) {
43
50
  return ctx;
44
51
  }
45
52
  //#endregion
46
- export { resolveBaseUrl as a, getPageContext as i, FEATURE_FLAGS_BASE as n, sanitizeUrl as o, URLS as r, ANALYTICS_BASE as t };
53
+ export { sanitizeUrl as a, resolveBaseUrl as i, URLS as n, getPageContext as r, ANALYTICS_BASE as t };
@@ -0,0 +1,51 @@
1
+ //#region src/utils/session-manager.d.ts
2
+ type SessionContext = {
3
+ sessionId: string;
4
+ windowId: string;
5
+ sessionStart: number;
6
+ };
7
+ //#endregion
8
+ //#region src/extensions.d.ts
9
+ type ExtensionHooks = {
10
+ pageChange: {
11
+ url: string;
12
+ };
13
+ pageHide: {
14
+ persisted: boolean;
15
+ terminal: boolean;
16
+ };
17
+ pageShow: {
18
+ persisted: boolean;
19
+ };
20
+ consentChange: {
21
+ cookieless: boolean;
22
+ };
23
+ error: {
24
+ error: Error;
25
+ };
26
+ };
27
+ type ExtensionHook = keyof ExtensionHooks;
28
+ type ExtensionHandler<K extends ExtensionHook> = (event: ExtensionHooks[K]) => void | Promise<void>;
29
+ interface AnalyticsExtensionContext {
30
+ readonly siteKey: string;
31
+ readonly baseUrl: string;
32
+ readonly debug: boolean;
33
+ readonly trackHash: boolean;
34
+ readonly sdkName?: string;
35
+ readonly sdkVersion?: string;
36
+ isCookieless(): boolean;
37
+ getAnonymousId(): string;
38
+ getSession(): SessionContext;
39
+ capture(event: string, properties?: Record<string, unknown>): void;
40
+ on<K extends ExtensionHook>(hook: K, handler: ExtensionHandler<K>): () => void;
41
+ log(message: string, ...details: unknown[]): void;
42
+ }
43
+ interface AnalyticsExtension {
44
+ readonly name: string;
45
+ setup(context: AnalyticsExtensionContext): ExtensionCleanup | undefined | Promise<ExtensionCleanup | undefined>;
46
+ }
47
+ type ExtensionCleanup = (options: {
48
+ discard: boolean;
49
+ }) => void;
50
+ //#endregion
51
+ export { ExtensionHook as a, ExtensionHandler as i, AnalyticsExtensionContext as n, ExtensionHooks as o, ExtensionCleanup as r, AnalyticsExtension as t };
@@ -1,4 +1,4 @@
1
- import { d as setStorageItem, i as isCookielessMode, l as getStorageItem, n as createId, u as removeStorageItem } from "./send-data-DR1JUXka.js";
1
+ import { c as removeStorageItem, l as setStorageItem, n as createId, s as getStorageItem } from "./send-data-DFZnsaL2.js";
2
2
  //#region src/utils/identifiers.ts
3
3
  const ANONYMOUS_ID_KEY = "faststats_anon_id_";
4
4
  function getOrCreateAnonymousId(siteKey) {
@@ -8,8 +8,8 @@ function getOrCreateAnonymousId(siteKey) {
8
8
  const newId = createId();
9
9
  return setStorageItem("localStorage", key, newId) ? newId : "";
10
10
  }
11
- function getAnonymousId(siteKey, cookieless) {
12
- if (cookieless ?? isCookielessMode()) return "";
11
+ function getAnonymousId(siteKey, cookieless = false) {
12
+ if (cookieless) return "";
13
13
  return getOrCreateAnonymousId(siteKey);
14
14
  }
15
15
  function resetAnonymousId(siteKey, cookieless) {
@@ -28,18 +28,38 @@ function removeStorageItem(name, key) {
28
28
  }
29
29
  //#endregion
30
30
  //#region src/utils/session-manager.ts
31
- const IDLE_MS = 1800 * 1e3;
32
- const MAX_AGE_MS = 1440 * 60 * 1e3;
31
+ const IDLE_MS = 18e5;
32
+ const MAX_AGE_MS = 864e5;
33
33
  const SESSION_KEY = "faststats_session_";
34
- let cookielessMode = false;
34
+ const NO_STORAGE = {};
35
35
  const memorySessions = /* @__PURE__ */ new Map();
36
- const rotationListeners = /* @__PURE__ */ new Set();
36
+ const rotationListeners = /* @__PURE__ */ new Map();
37
+ function memoryKey(siteKey, cookieless) {
38
+ return `${siteKey}:${cookieless ? "memory" : "persistent"}`;
39
+ }
40
+ function storageScope() {
41
+ try {
42
+ return globalThis.localStorage ?? NO_STORAGE;
43
+ } catch {
44
+ return NO_STORAGE;
45
+ }
46
+ }
47
+ function readMemory(siteKey, cookieless) {
48
+ const entry = memorySessions.get(memoryKey(siteKey, cookieless));
49
+ return entry?.scope === storageScope() ? entry.session : null;
50
+ }
51
+ function writeMemory(siteKey, cookieless, session) {
52
+ memorySessions.set(memoryKey(siteKey, cookieless), {
53
+ scope: storageScope(),
54
+ session
55
+ });
56
+ }
37
57
  function createId() {
38
58
  if (typeof crypto !== "undefined" && "randomUUID" in crypto) return crypto.randomUUID();
39
59
  return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2)}`;
40
60
  }
41
61
  function readSession(siteKey) {
42
- const memory = memorySessions.get(siteKey) ?? null;
62
+ const memory = readMemory(siteKey, false);
43
63
  const raw = getStorageItem("localStorage", `${SESSION_KEY}${siteKey}`);
44
64
  if (!raw) return memory;
45
65
  try {
@@ -49,29 +69,29 @@ function readSession(siteKey) {
49
69
  return memory;
50
70
  }
51
71
  function writeSession(siteKey, session) {
52
- if (!setStorageItem("localStorage", `${SESSION_KEY}${siteKey}`, JSON.stringify(session))) memorySessions.set(siteKey, session);
72
+ if (!setStorageItem("localStorage", `${SESSION_KEY}${siteKey}`, JSON.stringify(session))) writeMemory(siteKey, false, session);
53
73
  }
54
- function clearSession(siteKey) {
55
- memorySessions.delete(siteKey);
56
- removeStorageItem("localStorage", `${SESSION_KEY}${siteKey}`);
74
+ function clearSession(siteKey, cookieless) {
75
+ memorySessions.delete(memoryKey(siteKey, cookieless));
76
+ if (!cookieless) removeStorageItem("localStorage", `${SESSION_KEY}${siteKey}`);
57
77
  }
58
78
  function expired(session, now) {
59
79
  return now - session.activity >= IDLE_MS || now - session.start >= MAX_AGE_MS;
60
80
  }
61
- function emitRotation(prev, next) {
62
- for (const listener of rotationListeners) listener(prev, next);
81
+ function emitRotation(siteKey, cookieless, prev, next) {
82
+ for (const listener of rotationListeners.get(memoryKey(siteKey, cookieless)) ?? []) listener(prev, next);
63
83
  }
64
84
  function resolveSession(siteKey, cookieless, touch) {
65
85
  const now = Date.now();
66
86
  if (cookieless) {
67
- let session = memorySessions.get(siteKey);
87
+ let session = readMemory(siteKey, true) ?? void 0;
68
88
  if (!session) {
69
89
  session = {
70
90
  id: createId(),
71
91
  start: now,
72
92
  activity: now
73
93
  };
74
- memorySessions.set(siteKey, session);
94
+ writeMemory(siteKey, true, session);
75
95
  } else if (touch) session.activity = now;
76
96
  return {
77
97
  session,
@@ -115,52 +135,50 @@ function toContext(siteKey, session, cookieless, windowIdOverride) {
115
135
  sessionStart: session.start
116
136
  };
117
137
  }
118
- function setCookielessMode(mode) {
119
- if (cookielessMode === mode) return;
120
- cookielessMode = mode;
121
- memorySessions.clear();
122
- }
123
- function isCookielessMode() {
124
- return cookielessMode;
125
- }
126
- function getSessionContext(siteKey, cookieless) {
127
- const useCookieless = cookieless ?? cookielessMode;
128
- const { session, rotatedFrom } = resolveSession(siteKey, useCookieless, false);
129
- const next = toContext(siteKey, session, useCookieless);
130
- if (rotatedFrom) emitRotation(toContext(siteKey, rotatedFrom, useCookieless, next.windowId), next);
138
+ function getSessionContext(siteKey, cookieless = false) {
139
+ const { session, rotatedFrom } = resolveSession(siteKey, cookieless, false);
140
+ const next = toContext(siteKey, session, cookieless);
141
+ if (rotatedFrom) emitRotation(siteKey, cookieless, toContext(siteKey, rotatedFrom, cookieless, next.windowId), next);
131
142
  return next;
132
143
  }
133
- function touchActivity(siteKey, cookieless) {
134
- const useCookieless = cookieless ?? cookielessMode;
135
- const { session, rotatedFrom } = resolveSession(siteKey, useCookieless, true);
144
+ function touchActivity(siteKey, cookieless = false) {
145
+ const { session, rotatedFrom } = resolveSession(siteKey, cookieless, true);
136
146
  if (!rotatedFrom) return null;
137
- const wid = windowId(siteKey, useCookieless, session.id);
147
+ const wid = windowId(siteKey, cookieless, session.id);
138
148
  const rotation = {
139
- prev: toContext(siteKey, rotatedFrom, useCookieless, wid),
140
- next: toContext(siteKey, session, useCookieless, wid)
149
+ prev: toContext(siteKey, rotatedFrom, cookieless, wid),
150
+ next: toContext(siteKey, session, cookieless, wid)
141
151
  };
142
- emitRotation(rotation.prev, rotation.next);
152
+ emitRotation(siteKey, cookieless, rotation.prev, rotation.next);
143
153
  return rotation;
144
154
  }
145
- function resetSession(siteKey) {
146
- const useCookieless = cookielessMode;
147
- const previous = getSessionContext(siteKey, useCookieless);
148
- clearSession(siteKey);
149
- if (useCookieless) {
155
+ function resetSession(siteKey, cookieless = false) {
156
+ const previous = getSessionContext(siteKey, cookieless);
157
+ clearSession(siteKey, cookieless);
158
+ if (cookieless) {
150
159
  const now = Date.now();
151
- memorySessions.set(siteKey, {
160
+ writeMemory(siteKey, true, {
152
161
  id: createId(),
153
162
  start: now,
154
163
  activity: now
155
164
  });
156
165
  } else removeStorageItem("sessionStorage", `faststats_window_id_${siteKey}`);
157
- const next = getSessionContext(siteKey, useCookieless);
158
- if (previous.sessionId !== next.sessionId) emitRotation(previous, next);
166
+ const next = getSessionContext(siteKey, cookieless);
167
+ if (previous.sessionId !== next.sessionId) emitRotation(siteKey, cookieless, previous, next);
159
168
  return next;
160
169
  }
161
- function onSessionRotated(listener) {
162
- rotationListeners.add(listener);
163
- return () => rotationListeners.delete(listener);
170
+ function onSessionRotated(siteKey, cookieless, listener) {
171
+ const key = memoryKey(siteKey, cookieless);
172
+ let listeners = rotationListeners.get(key);
173
+ if (!listeners) {
174
+ listeners = /* @__PURE__ */ new Set();
175
+ rotationListeners.set(key, listeners);
176
+ }
177
+ listeners.add(listener);
178
+ return () => {
179
+ listeners?.delete(listener);
180
+ if (listeners?.size === 0) rotationListeners.delete(key);
181
+ };
164
182
  }
165
183
  //#endregion
166
184
  //#region src/utils/send-data.ts
@@ -198,4 +216,4 @@ async function sendData(options) {
198
216
  }
199
217
  }
200
218
  //#endregion
201
- export { onSessionRotated as a, touchActivity as c, setStorageItem as d, isCookielessMode as i, getStorageItem as l, createId as n, resetSession as o, getSessionContext as r, setCookielessMode as s, sendData as t, removeStorageItem as u };
219
+ export { resetSession as a, removeStorageItem as c, onSessionRotated as i, setStorageItem as l, createId as n, touchActivity as o, getSessionContext as r, getStorageItem as s, sendData as t };
package/dist/error.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { t as AnalyticsExtension } from "./chunks/extensions-DW7tJY0T.js";
1
2
  //#region src/error.d.ts
2
3
  type ErrorEntry = {
3
4
  error: string;
@@ -10,11 +11,14 @@ interface ErrorTrackingOptions {
10
11
  baseUrl?: string;
11
12
  debug?: boolean;
12
13
  trackHash?: boolean;
14
+ cookieless?: boolean;
13
15
  flushInterval?: number;
14
16
  maxQueueSize?: number;
15
17
  sdkName?: string;
16
18
  sdkVersion?: string;
17
19
  }
20
+ type ErrorTrackingExtensionOptions = Omit<ErrorTrackingOptions, "siteKey" | "baseUrl" | "debug" | "trackHash" | "cookieless" | "sdkName" | "sdkVersion">;
21
+ declare function errorTracking(options?: ErrorTrackingExtensionOptions): AnalyticsExtension;
18
22
  declare class ErrorTracker {
19
23
  private readonly options;
20
24
  private readonly endpoint;
@@ -22,8 +26,9 @@ declare class ErrorTracker {
22
26
  private timer;
23
27
  private started;
24
28
  private flushing;
29
+ private cookieless;
25
30
  constructor(options: ErrorTrackingOptions);
26
- private log;
31
+ setCookielessMode(cookieless: boolean): void;
27
32
  start(): void;
28
33
  stop(discard?: boolean): void;
29
34
  onPageHidden(persisted?: boolean): void;
@@ -34,4 +39,4 @@ declare class ErrorTracker {
34
39
  private flush;
35
40
  }
36
41
  //#endregion
37
- export { ErrorEntry, ErrorTrackingOptions, ErrorTracker as default };
42
+ export { ErrorEntry, ErrorTrackingExtensionOptions, ErrorTrackingOptions, ErrorTracker as default, errorTracking };
package/dist/error.js CHANGED
@@ -1,10 +1,32 @@
1
- import { a as resolveBaseUrl, i as getPageContext, r as URLS, t as ANALYTICS_BASE } from "./chunks/api-urls-Dp2XNjRF.js";
2
- import { r as getSessionContext, t as sendData } from "./chunks/send-data-DR1JUXka.js";
3
- import { t as getAnonymousId } from "./chunks/identifiers-BvRNbiwA.js";
1
+ import { i as resolveBaseUrl, n as URLS, r as getPageContext, t as ANALYTICS_BASE } from "./chunks/api-urls-DWk43fu6.js";
2
+ import { r as getSessionContext, t as sendData } from "./chunks/send-data-DFZnsaL2.js";
3
+ import { t as getAnonymousId } from "./chunks/identifiers-KgQDX74Q.js";
4
4
  //#region src/error.ts
5
5
  const SDK_NAME = "@faststats/web";
6
- const SDK_VERSION = "0.7.0";
7
- const EXTENSION_RE = /(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//;
6
+ const SDK_VERSION = "0.8.1";
7
+ function errorTracking(options = {}) {
8
+ return {
9
+ name: "error-tracking",
10
+ setup(context) {
11
+ const tracker = new ErrorTracker({
12
+ ...options,
13
+ siteKey: context.siteKey,
14
+ baseUrl: context.baseUrl,
15
+ debug: context.debug,
16
+ trackHash: context.trackHash,
17
+ cookieless: context.isCookieless(),
18
+ sdkName: context.sdkName,
19
+ sdkVersion: context.sdkVersion
20
+ });
21
+ context.on("pageHide", ({ persisted }) => tracker.onPageHidden(persisted));
22
+ context.on("error", ({ error }) => tracker.captureError(error));
23
+ context.on("consentChange", ({ cookieless }) => tracker.setCookielessMode(cookieless));
24
+ tracker.start();
25
+ return ({ discard }) => tracker.stop(discard);
26
+ }
27
+ };
28
+ }
29
+ const EXTENSION_URL = /(?:^|\()(chrome|moz|ms-browser|safari-web)-extension:\/\//;
8
30
  function toEntry(kind, source, handled, fallback) {
9
31
  if (source instanceof Error) return {
10
32
  error: kind === "UnhandledRejection" ? kind : source.name.trim() || kind,
@@ -20,7 +42,7 @@ function toEntry(kind, source, handled, fallback) {
20
42
  }
21
43
  function isExtensionError(filename, source) {
22
44
  const stack = source instanceof Error ? source.stack ?? "" : "";
23
- return EXTENSION_RE.test(`${filename}\n${stack}`);
45
+ return EXTENSION_URL.test(filename) || EXTENSION_URL.test(stack);
24
46
  }
25
47
  var ErrorTracker = class {
26
48
  options;
@@ -29,12 +51,15 @@ var ErrorTracker = class {
29
51
  timer = null;
30
52
  started = false;
31
53
  flushing = false;
54
+ cookieless;
32
55
  constructor(options) {
33
56
  this.options = options;
34
57
  this.endpoint = `${resolveBaseUrl(options.baseUrl, ANALYTICS_BASE)}${URLS.events}`;
58
+ this.cookieless = options.cookieless ?? false;
35
59
  }
36
- log(...args) {
37
- if (this.options.debug) console.log("[ErrorTracker]", ...args);
60
+ setCookielessMode(cookieless) {
61
+ if (this.cookieless !== cookieless) this.queue = [];
62
+ this.cookieless = cookieless;
38
63
  }
39
64
  start() {
40
65
  if (this.started || typeof window === "undefined") return;
@@ -42,7 +67,6 @@ var ErrorTracker = class {
42
67
  window.addEventListener("error", this.onError);
43
68
  window.addEventListener("unhandledrejection", this.onRejection);
44
69
  this.timer = setInterval(() => void this.flush(), this.options.flushInterval ?? 5e3);
45
- this.log("Started");
46
70
  }
47
71
  stop(discard = false) {
48
72
  if (!this.started || typeof window === "undefined") return;
@@ -53,7 +77,6 @@ var ErrorTracker = class {
53
77
  this.timer = null;
54
78
  if (discard) this.queue = [];
55
79
  else this.flush();
56
- this.log("Stopped");
57
80
  }
58
81
  onPageHidden(persisted = false) {
59
82
  if (!persisted) this.flush();
@@ -71,9 +94,8 @@ var ErrorTracker = class {
71
94
  };
72
95
  enqueue(error) {
73
96
  const buildId = globalThis.__SOURCEMAPS_BUILD__?.buildId;
74
- const userId = getAnonymousId(this.options.siteKey);
75
- const ctx = getSessionContext(this.options.siteKey);
76
- this.log("Captured:", error);
97
+ const userId = getAnonymousId(this.options.siteKey, this.cookieless);
98
+ const ctx = getSessionContext(this.options.siteKey, this.cookieless);
77
99
  this.queue.push({
78
100
  error,
79
101
  context: {
@@ -132,4 +154,4 @@ var ErrorTracker = class {
132
154
  }
133
155
  };
134
156
  //#endregion
135
- export { ErrorTracker as default };
157
+ export { ErrorTracker as default, errorTracking };
package/dist/index.d.ts CHANGED
@@ -1,60 +1,40 @@
1
- import { FeatureFlagEvaluation } from "./feature-flags.js";
2
- import { n as ReplayTrackerOptions } from "./chunks/replay-BeC2XA4N.js";
3
-
1
+ import { a as ExtensionHook, i as ExtensionHandler, n as AnalyticsExtensionContext, o as ExtensionHooks, r as ExtensionCleanup, t as AnalyticsExtension } from "./chunks/extensions-DW7tJY0T.js";
4
2
  //#region src/analytics.d.ts
5
- type ConsentMode = "pending" | "granted" | "denied";
6
- type PendingBehavior = "anonymous" | "disabled";
3
+ type ConsentMode = "anonymous" | "granted" | "denied";
7
4
  interface IdentifyOptions {
8
- name?: string;
9
- phone?: string;
10
- avatarUrl?: string;
5
+ name?: string | null;
6
+ phone?: string | null;
7
+ avatarUrl?: string | null;
11
8
  traits?: Record<string, unknown>;
9
+ traitMode?: "merge" | "replace";
10
+ unsetTraits?: string[];
11
+ aliases?: string[];
12
+ }
13
+ interface IdentifyUser extends IdentifyOptions {
14
+ id: string;
15
+ email?: string | null;
12
16
  }
13
17
  interface WebAnalyticsOptions {
14
18
  siteKey: string;
15
19
  baseUrl?: string;
16
- featureFlagsBaseUrl?: string;
17
20
  debug?: boolean;
18
- autoTrack?: boolean;
19
21
  trackHash?: boolean;
20
22
  cookieless?: boolean;
21
- consent?: {
22
- mode?: ConsentMode;
23
- pendingBehavior?: PendingBehavior;
24
- };
25
- webVitals?: {
26
- enabled?: boolean;
27
- attribution?: boolean;
28
- };
29
- sessionReplays?: {
30
- enabled?: boolean;
31
- };
32
- errorTracking?: {
33
- enabled?: boolean;
34
- };
35
- replayOptions?: Partial<ReplayTrackerOptions>;
23
+ consent?: ConsentMode;
24
+ extensions?: readonly AnalyticsExtension[];
36
25
  /** @internal */
37
26
  sdkName?: string;
38
27
  /** @internal */
39
28
  sdkVersion?: string;
40
29
  }
41
- declare function getInstance(): WebAnalytics | null;
42
- declare function trackEvent(eventName: string, properties?: Record<string, unknown>): void;
43
- declare function identify(externalId: string, email: string, options?: IdentifyOptions): Promise<boolean>;
44
- declare function logout(resetAnonymousIdentity?: boolean): void;
45
- declare function setConsentMode(mode: ConsentMode): void;
46
- declare function optIn(): void;
47
- declare function optOut(): void;
48
- declare function reportError(error: Error): void;
49
- declare function isTrackingDisabled(): boolean;
50
30
  declare class WebAnalytics {
51
31
  private readonly options;
52
32
  private readonly baseUrl;
53
33
  private readonly debug;
34
+ private readonly cleanup;
54
35
  private started;
55
- private destroyed;
56
- private epoch;
57
36
  private heartbeatTimer;
37
+ private extensions;
58
38
  private pageKey;
59
39
  private entry;
60
40
  private left;
@@ -62,51 +42,53 @@ declare class WebAnalytics {
62
42
  private visitStartedAt;
63
43
  private pendingPageviewTrigger;
64
44
  private consentMode;
65
- private readonly pendingBehavior;
66
- private readonly cleanup;
67
- private readonly childTrackers;
68
- private readonly outboundLinks;
69
45
  constructor(options: WebAnalyticsOptions);
70
- private log;
71
- private isTrackingBlocked;
72
- private ensureActive;
73
- private canTrack;
74
- private canSendEvents;
75
- private cookieless;
76
- private touch;
77
- private bind;
78
- private stopHeartbeat;
79
- private loadChild;
80
- private loadOptionalTrackers;
81
- private enterPage;
82
- private beginTracking;
83
- private readonly onLinkClick;
84
- private readonly onVisibilityChange;
85
- private readonly onPageHide;
86
- private readonly onPageShow;
87
- start(): Promise<void>;
46
+ start(): void;
88
47
  destroy(): void;
89
- pageview(extra?: Record<string, unknown>): void;
90
- track(name: string, extra?: Record<string, unknown>): void;
91
- identify(externalId: string, email: string, options?: IdentifyOptions): Promise<boolean>;
48
+ pageview(properties?: Record<string, unknown>): void;
49
+ track(name: string, properties?: Record<string, unknown>): void;
50
+ identify(user: IdentifyUser): Promise<boolean>;
51
+ identify(externalId: string, email?: string, options?: IdentifyOptions): Promise<boolean>;
92
52
  logout(resetAnonymousIdentity?: boolean): void;
93
53
  setConsentMode(mode: ConsentMode): void;
94
- getConsentMode(): ConsentMode;
95
54
  getAnonymousId(): string;
96
- getSessionId(): string;
97
- getWindowId(): string;
98
- checkFeatureFlag(key: string, attributes?: Record<string, unknown>, opts?: {
99
- externalId?: string;
100
- signal?: AbortSignal;
101
- }): Promise<FeatureFlagEvaluation>;
102
55
  reportError(error: Error): void;
103
- private send;
56
+ private canTrack;
57
+ private isCookieless;
58
+ private ensureActive;
59
+ private activate;
60
+ private startExtensions;
61
+ private stopExtensions;
62
+ private installBrowserLifecycle;
63
+ private bind;
64
+ private readonly onNavigate;
65
+ private readonly onVisibilityChange;
66
+ private readonly onPageHide;
67
+ private readonly onPageShow;
68
+ private navigate;
69
+ private enterPage;
104
70
  private leavePage;
105
- private startVisit;
106
71
  private pauseVisit;
107
72
  private resumeVisit;
73
+ private touch;
108
74
  private startHeartbeat;
109
- private navigate;
75
+ private stopHeartbeat;
76
+ private send;
110
77
  }
111
78
  //#endregion
112
- export { type ConsentMode, type FeatureFlagEvaluation, type IdentifyOptions, type PendingBehavior, WebAnalytics, type WebAnalyticsOptions, getInstance, identify, isTrackingDisabled, logout, optIn, optOut, reportError, setConsentMode, trackEvent };
79
+ //#region src/client.d.ts
80
+ /** Creates an independent client. Call `start()` when it should begin tracking. */
81
+ declare function createClient(options: WebAnalyticsOptions): WebAnalytics;
82
+ /** Initializes and starts the shared client used by the convenience functions. */
83
+ declare function init(options: WebAnalyticsOptions): WebAnalytics;
84
+ /** Stops and forgets the shared client. Independent clients are unaffected. */
85
+ declare function shutdown(): void;
86
+ declare function pageview(properties?: Record<string, unknown>): void;
87
+ declare function track(name: string, properties?: Record<string, unknown>): void;
88
+ declare function identify(user: IdentifyUser): Promise<boolean>;
89
+ declare function identify(externalId: string, email?: string, options?: IdentifyOptions): Promise<boolean>;
90
+ declare function logout(resetAnonymousIdentity?: boolean): void;
91
+ declare function setConsentMode(mode: ConsentMode): void;
92
+ declare function reportError(error: Error): void;
93
+ //#endregion
94
+ export { type AnalyticsExtension, type AnalyticsExtensionContext, type ConsentMode, type ExtensionCleanup, type ExtensionHandler, type ExtensionHook, type ExtensionHooks, type IdentifyOptions, type IdentifyUser, WebAnalytics, type WebAnalyticsOptions, createClient, identify, init, logout, pageview, reportError, setConsentMode, shutdown, track };