@v-tilt/browser 1.11.0 → 1.13.0

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.
Files changed (90) hide show
  1. package/dist/all-external-dependencies.js +1 -1
  2. package/dist/all-external-dependencies.js.map +1 -1
  3. package/dist/array.full.js +1 -1
  4. package/dist/array.full.js.map +1 -1
  5. package/dist/array.js +1 -1
  6. package/dist/array.js.map +1 -1
  7. package/dist/array.no-external.js +1 -1
  8. package/dist/array.no-external.js.map +1 -1
  9. package/dist/autocapture-types.d.ts +17 -0
  10. package/dist/autocapture-utils.d.ts +24 -1
  11. package/dist/autocapture.d.ts +94 -5
  12. package/dist/chat.js +1 -1
  13. package/dist/chat.js.map +1 -1
  14. package/dist/config.d.ts +8 -1
  15. package/dist/constants.d.ts +19 -13
  16. package/dist/core/capture.d.ts +15 -5
  17. package/dist/core/config-utils.d.ts +15 -0
  18. package/dist/core/consent.d.ts +62 -0
  19. package/dist/core/event-buffer.d.ts +60 -0
  20. package/dist/core/fb-cookies.d.ts +32 -0
  21. package/dist/core/feature-manager.d.ts +61 -69
  22. package/dist/core/fifo-queue.d.ts +23 -0
  23. package/dist/core/identity.d.ts +23 -33
  24. package/dist/core/index.d.ts +7 -1
  25. package/dist/core/page-lifecycle.d.ts +41 -0
  26. package/dist/core/remote-config.d.ts +14 -17
  27. package/dist/extensions/chat/bubble-drag.d.ts +30 -0
  28. package/dist/extensions/chat/chat-api.d.ts +15 -0
  29. package/dist/extensions/chat/chat-styles.d.ts +27 -0
  30. package/dist/extensions/chat/chat-wrapper.d.ts +20 -145
  31. package/dist/extensions/chat/chat.d.ts +261 -14
  32. package/dist/extensions/chat/message-content-styles.d.ts +1 -0
  33. package/dist/extensions/chat/message-html.d.ts +6 -0
  34. package/dist/extensions/chat/message-markdown.d.ts +8 -0
  35. package/dist/extensions/chat/normalize-send-content.d.ts +24 -0
  36. package/dist/extensions/chat/types.d.ts +19 -57
  37. package/dist/extensions/chat/widget-registry.d.ts +53 -0
  38. package/dist/extensions/chat/widgets/collect-email.d.ts +6 -0
  39. package/dist/extensions/chat/widgets/escalate-to-human.d.ts +6 -0
  40. package/dist/extensions/ga4-proxy.d.ts +59 -0
  41. package/dist/extensions/google-tag-gateway/consent-bridge.d.ts +27 -0
  42. package/dist/extensions/google-tag-gateway/enhanced-conversions.d.ts +35 -0
  43. package/dist/extensions/google-tag-gateway/event-bridge.d.ts +74 -0
  44. package/dist/extensions/google-tag-gateway/google-tag-gateway.d.ts +95 -0
  45. package/dist/extensions/google-tag-gateway/gtag-loader.d.ts +85 -0
  46. package/dist/extensions/google-tag-gateway/index.d.ts +7 -0
  47. package/dist/extensions/google-tag-gateway/normalize.d.ts +28 -0
  48. package/dist/extensions/google-tag-gateway/public-api.d.ts +23 -0
  49. package/dist/extensions/history-autocapture.d.ts +2 -2
  50. package/dist/extensions/replay/index.d.ts +1 -1
  51. package/dist/extensions/replay/session-recording-utils.d.ts +13 -43
  52. package/dist/extensions/replay/session-recording-wrapper.d.ts +10 -66
  53. package/dist/extensions/replay/session-recording.d.ts +53 -1
  54. package/dist/extensions/replay/types.d.ts +6 -1
  55. package/dist/extensions/web-vitals/web-vitals-manager.d.ts +14 -43
  56. package/dist/external-scripts-loader.js +1 -1
  57. package/dist/external-scripts-loader.js.map +1 -1
  58. package/dist/feature.d.ts +54 -172
  59. package/dist/main.js +1 -1
  60. package/dist/main.js.map +1 -1
  61. package/dist/module.d.ts +728 -753
  62. package/dist/module.js +1 -1
  63. package/dist/module.js.map +1 -1
  64. package/dist/module.no-external.d.ts +728 -753
  65. package/dist/module.no-external.js +1 -1
  66. package/dist/module.no-external.js.map +1 -1
  67. package/dist/rate-limiter.d.ts +0 -1
  68. package/dist/recorder.js +1 -1
  69. package/dist/recorder.js.map +1 -1
  70. package/dist/request.d.ts +34 -20
  71. package/dist/scroll-depth-tracker.d.ts +42 -0
  72. package/dist/server.d.ts +114 -0
  73. package/dist/server.js +1 -1
  74. package/dist/server.js.map +1 -1
  75. package/dist/session.d.ts +12 -0
  76. package/dist/types.d.ts +204 -9
  77. package/dist/user-manager.d.ts +26 -52
  78. package/dist/utils/base64.d.ts +30 -0
  79. package/dist/utils/bot-detection.d.ts +28 -0
  80. package/dist/utils/endpoint-url.d.ts +36 -0
  81. package/dist/utils/event-emitter.d.ts +1 -0
  82. package/dist/utils/globals.d.ts +71 -2
  83. package/dist/utils/index.d.ts +20 -5
  84. package/dist/utils/logger.d.ts +66 -0
  85. package/dist/utils/request-utils.d.ts +5 -0
  86. package/dist/utils/safewrap.d.ts +6 -1
  87. package/dist/utils/transport-health.d.ts +55 -0
  88. package/dist/vtilt.d.ts +85 -25
  89. package/dist/web-vitals.js.map +1 -1
  90. package/package.json +71 -66
package/dist/config.d.ts CHANGED
@@ -3,7 +3,14 @@ export declare class ConfigManager {
3
3
  private config;
4
4
  constructor(config?: Partial<VTiltConfig>);
5
5
  /**
6
- * Parse configuration from script attributes
6
+ * Parse configuration from script attributes.
7
+ *
8
+ * IMPORTANT: do not back-fill `log_level` with a resolved default here.
9
+ * `log_level` in VTiltConfig represents the integrator's explicit intent —
10
+ * `setLevelFromConfig` (logger.ts) treats `log_level !== undefined` as
11
+ * "integrator pinned the level", which then locks remote /decide overrides.
12
+ * Baking in `'warn'` for every integrator who didn't pass anything would
13
+ * silently disable dashboard-driven log-level changes via diagnostics.
7
14
  */
8
15
  private parseConfigFromScript;
9
16
  /**
@@ -57,14 +57,6 @@ export declare const PERSISTENCE_METHODS: {
57
57
  sessionStorage: "sessionStorage";
58
58
  memory: "memory";
59
59
  };
60
- /** @deprecated Use PERSISTENCE_METHODS instead */
61
- export declare const STORAGE_METHODS: {
62
- cookie: "cookie";
63
- localStorage: "localStorage";
64
- localStoragePlusCookie: "localStorage+cookie";
65
- sessionStorage: "sessionStorage";
66
- memory: "memory";
67
- };
68
60
  /** Enable person processing flag */
69
61
  export declare const ENABLE_PERSON_PROCESSING = "$epp";
70
62
  /** Initial person info (referrer, URL, UTM params) */
@@ -87,23 +79,25 @@ export declare const EVENT_PAGELEAVE = "$pageleave";
87
79
  /** Identify event */
88
80
  export declare const EVENT_IDENTIFY = "$identify";
89
81
  /** Alias event */
90
- export declare const EVENT_ALIAS = "$create_alias";
82
+ export declare const EVENT_ALIAS = "$alias";
91
83
  /** Set person properties event */
92
84
  export declare const EVENT_SET = "$set";
93
- /** Group identify event */
94
- export declare const EVENT_GROUP_IDENTIFY = "$groupidentify";
95
85
  /** Performance event */
96
86
  export declare const EVENT_PERFORMANCE = "$performance_event";
87
+ /** Autocapture event */
88
+ export declare const EVENT_AUTOCAPTURE = "$autocapture";
89
+ /** Scroll depth milestone event (autocapture) */
90
+ export declare const EVENT_SCROLL_DEPTH = "$scroll_depth";
97
91
  /** Snapshot event (session recording) */
98
92
  export declare const EVENT_SNAPSHOT = "$snapshot";
93
+ /** Snapshot batch items event */
94
+ export declare const EVENT_SNAPSHOT_ITEMS = "$snapshot_items";
99
95
  /** Rate limit warning event */
100
96
  export declare const EVENT_RATE_LIMIT_WARNING = "$$client_ingestion_warning";
101
97
  /** Process person profile flag */
102
98
  export declare const PROP_PROCESS_PERSON_PROFILE = "$process_person_profile";
103
99
  /** Is identified flag */
104
100
  export declare const PROP_IS_IDENTIFIED = "$is_identified";
105
- /** Anonymous distinct ID */
106
- export declare const PROP_ANON_DISTINCT_ID = "$anon_distinct_id";
107
101
  /** Session ID property */
108
102
  export declare const PROP_SESSION_ID = "$session_id";
109
103
  /** Window ID property */
@@ -114,6 +108,18 @@ export declare const PROP_CURRENT_URL = "$current_url";
114
108
  export declare const PROP_HOST = "$host";
115
109
  /** Pathname */
116
110
  export declare const PROP_PATHNAME = "$pathname";
111
+ /** Seconds on the page being left ($pageleave) */
112
+ export declare const PROP_PREV_PAGEVIEW_DURATION = "$prev_pageview_duration";
113
+ /** Pathname of the page being left */
114
+ export declare const PROP_PREV_PAGEVIEW_PATHNAME = "$prev_pageview_pathname";
115
+ /** URL of the page being left */
116
+ export declare const PROP_PREV_PAGEVIEW_URL = "$prev_pageview_url";
117
+ /** Scroll depth milestone threshold (0–100) */
118
+ export declare const PROP_SCROLL_DEPTH_PCT = "$scroll_depth_pct";
119
+ /** Max scroll depth % at time of milestone event */
120
+ export declare const PROP_SCROLL_DEPTH_MAX_PCT = "$scroll_depth_max_pct";
121
+ /** Max scroll depth % on the page being left ($pageleave) */
122
+ export declare const PROP_PREV_PAGEVIEW_SCROLL_DEPTH_PCT = "$prev_pageview_scroll_depth_pct";
117
123
  /** Referrer URL */
118
124
  export declare const PROP_REFERRER = "$referrer";
119
125
  /** Referring domain */
@@ -15,21 +15,31 @@ export interface CaptureHost {
15
15
  setSessionId(): void;
16
16
  getSessionId(): string | null;
17
17
  getWindowId(): string;
18
+ consumeSessionStart(): boolean;
18
19
  };
19
20
  userManager: {
20
21
  getAnonymousId(): string;
21
22
  getDistinctId(): string | null;
22
23
  getUserProperties(): Record<string, any>;
23
24
  get_initial_props(): Record<string, any>;
25
+ consumeFirstVisit(): boolean;
26
+ };
27
+ consentManager?: {
28
+ hasConsent(): boolean;
29
+ getConsentProperties(): Record<string, boolean>;
24
30
  };
25
31
  rateLimiter: {
26
32
  shouldAllowEvent(): boolean;
27
33
  };
28
- sendRequest(url: string, event: TrackingEvent, shouldEnqueue?: boolean): void;
34
+ sendRequest(url: string, event: TrackingEvent): void;
35
+ bufferEvent(name: string, url: string, event: TrackingEvent): void;
29
36
  buildUrl(): string;
37
+ buildEndpointUrl(path: string): string;
30
38
  }
31
39
  export interface CaptureOptions {
32
40
  skip_client_rate_limiting?: boolean;
41
+ /** When true, do not add inter-capture engagement_time_msec (e.g. $pageleave uses dwell). */
42
+ skip_engagement?: boolean;
33
43
  }
34
44
  export declare class CaptureManager {
35
45
  private _host;
@@ -37,6 +47,7 @@ export declare class CaptureManager {
37
47
  private _initial_pageview_captured;
38
48
  private _visibility_state_listener;
39
49
  private _vt_person_id;
50
+ private readonly _pageLifecycle;
40
51
  constructor(host: CaptureHost);
41
52
  /** Set the vt person ID from URL parameter */
42
53
  setVtPersonId(id: string | null): void;
@@ -54,15 +65,14 @@ export declare class CaptureManager {
54
65
  * Used for system events like rate limit warnings.
55
66
  */
56
67
  captureInternal(name: string, payload: EventPayload): void;
57
- /**
58
- * Track a custom event (alias for capture).
59
- */
60
- trackEvent(name: string, payload?: EventPayload): void;
61
68
  /**
62
69
  * Capture initial pageview with visibility check.
63
70
  * Waits for page to become visible before capturing.
64
71
  */
65
72
  captureInitialPageview(): void;
73
+ tryCapturePageleave(reason: string, extraProps?: EventPayload): void;
74
+ resetPageleaveDedupe(): void;
75
+ private _isDebugMode;
66
76
  /**
67
77
  * Build enriched event payload with all common properties.
68
78
  */
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolved config helpers (VTiltConfig may contain sentinel values like
3
+ * `if_capture_pageview` that are not booleans until interpreted).
4
+ */
5
+ import type { VTiltConfig } from "../types";
6
+ /**
7
+ * Whether automatic $pageleave should run.
8
+ * - `capture_pageleave === false` → off
9
+ * - `capture_pageleave === true` → on
10
+ * - `undefined` or `'if_capture_pageview'` → on iff pageviews are enabled
11
+ *
12
+ * Server-side $config_pending filter handles the edge case where events
13
+ * are captured before fresh remote config loads.
14
+ */
15
+ export declare function isPageleaveEnabled(config: VTiltConfig): boolean;
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Consent Manager
3
+ *
4
+ * Manages user consent state for analytics, marketing, and advertising.
5
+ * Stores consent in a first-party cookie (_vtilt_consent) and provides
6
+ * consent properties to attach to every event.
7
+ *
8
+ * When `requireConsent` is true in config, the CaptureManager gates
9
+ * non-essential events until setConsent() is called.
10
+ */
11
+ export interface ConsentState {
12
+ analytics?: boolean;
13
+ marketing?: boolean;
14
+ advertising?: boolean;
15
+ }
16
+ export interface ConsentHost {
17
+ _emitter?: {
18
+ emit(event: string, payload?: unknown): void;
19
+ };
20
+ getConfig(): {
21
+ require_consent?: boolean;
22
+ };
23
+ }
24
+ export declare class ConsentManager {
25
+ private _host;
26
+ private _state;
27
+ private _hasBeenSet;
28
+ constructor(host: ConsentHost);
29
+ /**
30
+ * Set consent state. Merges with existing state.
31
+ * Persists to cookie and emits consent:updated.
32
+ */
33
+ setConsent(consent: ConsentState): void;
34
+ /**
35
+ * Get current consent state.
36
+ * Returns undefined fields for categories not yet set.
37
+ */
38
+ getConsent(): ConsentState;
39
+ /**
40
+ * Whether setConsent() has been called (or consent was loaded from cookie).
41
+ * Used by CaptureManager to gate events when requireConsent is true.
42
+ */
43
+ hasConsent(): boolean;
44
+ /**
45
+ * Apply default-all-granted state when no explicit consent exists.
46
+ * Sets all categories to true in memory but does NOT persist to cookie
47
+ * (implicit defaults don't need storage — only explicit user choices do).
48
+ */
49
+ setDefaultGranted(): void;
50
+ /**
51
+ * Returns event properties to attach to every captured event.
52
+ * Only includes fields that have been explicitly set.
53
+ */
54
+ getConsentProperties(): Record<string, boolean>;
55
+ /**
56
+ * Reset consent state (e.g. on user logout).
57
+ */
58
+ reset(): void;
59
+ private _readFromCookie;
60
+ private _writeToCookie;
61
+ private _removeCookie;
62
+ }
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Event Buffer
3
+ *
4
+ * Buffers events until fresh remote config is resolved. Ensures every
5
+ * event respects current project config.
6
+ *
7
+ * Ready condition:
8
+ * - _configReady: fresh /api/d response applied (or fetch failed)
9
+ *
10
+ * When config IS ready, queued events are filtered against the resolved
11
+ * analytics toggles (capture_pageview, capture_pageleave, etc.) and
12
+ * dropped if disabled — mirroring the server-side $config_pending filter.
13
+ *
14
+ * Events that flush before config is ready (safety timeout, page unload)
15
+ * are tagged with $config_pending for server-side filtering.
16
+ */
17
+ import type { TrackingEvent, VTiltConfig } from "../types";
18
+ export interface BufferedEvent {
19
+ name: string;
20
+ url: string;
21
+ event: TrackingEvent;
22
+ }
23
+ export interface EventBufferHost {
24
+ sendRequest(url: string, event: TrackingEvent): void;
25
+ requestQueue: {
26
+ flush(): void;
27
+ };
28
+ getConfig(): VTiltConfig;
29
+ }
30
+ export declare class EventBuffer {
31
+ private _host;
32
+ private readonly _queue;
33
+ private _ready;
34
+ private _configReady;
35
+ private _safetyTimer;
36
+ constructor(host: EventBufferHost);
37
+ /**
38
+ * Push an event into the buffer.
39
+ * If ready, processes inline (pass-through). Otherwise queues.
40
+ */
41
+ push(item: BufferedEvent): void;
42
+ /** Signal that fresh remote config has been loaded (or fetch failed). */
43
+ markConfigReady(): void;
44
+ /**
45
+ * Force-flush all buffered events (page unload / safety timeout).
46
+ * Tags events with $config_pending when config hasn't loaded.
47
+ */
48
+ forceFlush(): void;
49
+ private _checkReady;
50
+ private _tryFlush;
51
+ private _flush;
52
+ private _process;
53
+ /**
54
+ * Check whether the event should be dropped based on resolved config.
55
+ * Mirrors the server-side checkProjectConfigFilter logic.
56
+ */
57
+ private _isDisabledByConfig;
58
+ private _tagConfigPending;
59
+ private _clearSafetyTimer;
60
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Facebook Cookie Manager
3
+ *
4
+ * Manages _fbp (Facebook Browser ID) and _fbc (Facebook Click ID) cookies
5
+ * as first-party cookies for server-side Facebook CAPI integration.
6
+ *
7
+ * _fbp format: fb.1.{creation_timestamp_ms}.{random_10_digits}
8
+ * - Set on first visit, 180-day expiry (Meta spec)
9
+ *
10
+ * _fbc format: fb.1.{click_timestamp_ms}.{fbclid}
11
+ * - Set when fbclid URL parameter is present, 90-day expiry (Meta spec)
12
+ * - Persisted so CAPI events carry the correct click timestamp
13
+ */
14
+ /**
15
+ * Initialize Facebook cookies on SDK boot.
16
+ * - Generates _fbp if missing
17
+ * - Persists _fbc from fbclid URL parameter if present
18
+ */
19
+ export declare function initFacebookCookies(): void;
20
+ /**
21
+ * Read the current _fbp cookie value.
22
+ */
23
+ export declare function getFbp(): string | undefined;
24
+ /**
25
+ * Read the current _fbc cookie value.
26
+ */
27
+ export declare function getFbc(): string | undefined;
28
+ /**
29
+ * Remove both _fbp and _fbc cookies.
30
+ * Called when advertising consent is revoked.
31
+ */
32
+ export declare function removeFacebookCookies(): void;
@@ -1,95 +1,87 @@
1
1
  /**
2
2
  * Feature Manager
3
3
  *
4
- * Handles feature initialization, lifecycle management, and configuration
5
- * updates for all SDK features (HistoryAutocapture, Autocapture,
6
- * SessionRecording, Chat).
4
+ * Central registry for SDK features. Features self-describe via descriptors
5
+ * that declare their config keys, remote config mappings, and class references.
6
+ *
7
+ * Responsibilities:
8
+ * - Registration: features register descriptors before init
9
+ * - Instance creation: createInstances() constructs instances during init() (pre-boot)
10
+ * - Starting: initAll() starts created instances at boot via startIfEnabled
11
+ * - Config propagation: notifyAll() calls onConfigUpdate on each instance
12
+ * - Descriptor exposure: getDescriptors() lets RemoteConfigManager._apply iterate
13
+ *
14
+ * @see docs/patterns/tracker-feature-lifecycle.md
7
15
  */
8
- import { VTiltConfig, RemoteConfig } from "../types";
16
+ import type { VTiltConfig } from "../types";
9
17
  import type { Feature } from "../feature";
10
18
  /**
11
- * Interface for a feature class that can be instantiated.
19
+ * Self-describing metadata for a feature.
20
+ * Declared once at registration time; used by FeatureManager and RemoteConfigManager.
12
21
  */
13
- export interface FeatureClass<T extends Feature = Feature> {
14
- new (host: any, config?: any): T;
15
- extractConfig?(config: VTiltConfig): any;
16
- }
17
- /**
18
- * Feature registration entry
19
- */
20
- export interface FeatureEntry<T extends Feature = Feature> {
22
+ export interface FeatureDescriptor {
23
+ /** Unique name (used as map key and for get()) */
21
24
  name: string;
22
- instance?: T;
23
- FeatureClass: FeatureClass<T>;
24
- /** Whether the feature is in extensions folder (optional/lazy) */
25
- isExtension?: boolean;
26
- /** Config key to check for disabled state */
25
+ /** VTiltConfig key that holds this feature's config (e.g. "session_recording") */
26
+ configKey?: keyof VTiltConfig;
27
+ /** VTiltConfig boolean key that hard-disables the feature (e.g. "disable_session_recording") */
27
28
  disableKey?: keyof VTiltConfig;
28
- /** Config key that controls enabled state (e.g., 'autocapture') */
29
- enableKey?: keyof VTiltConfig;
29
+ /** Remote config mapping tells _apply how to extract config from RemoteConfig */
30
+ remoteConfig?: {
31
+ /** Key on the RemoteConfig object (e.g. "sessionRecording") */
32
+ key: string;
33
+ /** Transform remote section into the shape written to VTiltConfig[configKey] */
34
+ map: (remote: Record<string, unknown>) => Record<string, unknown>;
35
+ };
36
+ /** Feature class — must have a static extractConfig and a constructor */
37
+ FeatureClass: {
38
+ new (instance: any, config?: any): Feature;
39
+ extractConfig(config: VTiltConfig): any;
40
+ };
30
41
  }
31
42
  /**
32
- * Interface for the VTilt instance methods needed by FeatureManager.
43
+ * Minimal host interface to avoid circular dependency with VTilt.
33
44
  */
34
45
  export interface FeatureHost {
35
46
  getConfig(): VTiltConfig;
36
47
  }
37
48
  export declare class FeatureManager {
38
49
  private _host;
39
- private _features;
40
- private _onFeatureStarted?;
41
- private _onFeatureStopped?;
50
+ private _descriptors;
51
+ private _instances;
42
52
  constructor(host: FeatureHost);
43
- /** Set callback for when a feature starts */
44
- onFeatureStarted(callback: (name: string) => void): void;
45
- /** Set callback for when a feature stops */
46
- onFeatureStopped(callback: (name: string) => void): void;
53
+ /** Register a feature descriptor. Call before initAll(). */
54
+ register(desc: FeatureDescriptor): void;
47
55
  /**
48
- * Register a feature class for management.
56
+ * Create instances for all registered features without starting them.
57
+ * Safe to call before DOM boot — constructors are lightweight.
58
+ * Idempotent: skips features that already have an instance.
59
+ *
60
+ * Hard-disabled features are skipped (instance not created):
61
+ * - `disableKey` is true (e.g. `disable_chat: true`)
62
+ * - `configKey` section has `enabled: false` (e.g. `chat: { enabled: false }`)
49
63
  */
50
- register<T extends Feature>(entry: FeatureEntry<T>): void;
64
+ createInstances(): void;
51
65
  /**
52
- * Get a feature instance by name.
53
- */
54
- get<T extends Feature>(name: string): T | undefined;
55
- /**
56
- * Initialize all registered features.
66
+ * Create and start all registered features.
67
+ * Calls `createInstances()` first (idempotent), then `startIfEnabled()` on each.
57
68
  */
58
69
  initAll(): void;
70
+ /** Notify all initialized features of a config change. */
71
+ notifyAll(config: VTiltConfig): void;
72
+ /** Get a feature instance by name. */
73
+ get<T extends Feature>(name: string): T | undefined;
74
+ /** Register a late-created feature instance (e.g. from a public start*() call). */
75
+ set(name: string, instance: Feature): void;
76
+ /** Expose descriptors so RemoteConfigManager._apply can iterate for remote config mapping. */
77
+ getDescriptors(): Map<string, FeatureDescriptor>;
59
78
  /**
60
- * Initialize a specific feature by name.
61
- */
62
- init(name: string): Feature | undefined;
63
- /**
64
- * Start a feature manually by name.
65
- */
66
- start(name: string): void;
67
- /**
68
- * Stop a feature by name.
69
- */
70
- stop(name: string): void;
71
- /**
72
- * Check if a feature is active.
73
- */
74
- isActive(name: string): boolean;
75
- /**
76
- * Notify all features of configuration update.
77
- */
78
- notifyConfigUpdate(config: VTiltConfig): void;
79
- /**
80
- * Notify all features of remote configuration update.
81
- */
82
- notifyRemoteConfig(remote: RemoteConfig): void;
83
- /**
84
- * Get all feature instances.
85
- */
86
- getAll(): Feature[];
87
- /**
88
- * Initialize a single feature.
79
+ * True when the integrator explicitly hard-disabled this feature in code config.
80
+ * Two signals: `disableKey` flag (e.g. `disable_chat: true`) or
81
+ * `configKey` section with `enabled: false` (e.g. `chat: { enabled: false }`).
82
+ *
83
+ * When `enabled` is omitted (undefined), the feature may still auto-configure
84
+ * from dashboard settings, so we must construct the instance.
89
85
  */
90
- private _initFeature;
86
+ private _isHardDisabled;
91
87
  }
92
- /**
93
- * Create and configure a FeatureManager with standard vTilt features.
94
- */
95
- export declare function createFeatureManager(host: FeatureHost): FeatureManager;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * In-memory FIFO used by transport layers that must not drop ordering
3
+ * (EventBuffer, GoogleTagGateway pending captures).
4
+ *
5
+ * - Optional capacity: when full, {@link FifoQueue.enqueue} returns false
6
+ * and the item is not stored (same semantics as the previous hand-rolled
7
+ * `length < max` checks).
8
+ * - Default capacity is unbounded (`Number.POSITIVE_INFINITY`).
9
+ */
10
+ export declare class FifoQueue<T> {
11
+ private readonly _maxItems;
12
+ private _items;
13
+ constructor(_maxItems?: number);
14
+ get length(): number;
15
+ /** Append one item when under capacity. */
16
+ enqueue(item: T): boolean;
17
+ /**
18
+ * Atomically remove every item in arrival order. Returns a new array
19
+ * reference owned by the caller; the queue is empty after this call.
20
+ */
21
+ takeAll(): T[];
22
+ clear(): void;
23
+ }
@@ -1,74 +1,64 @@
1
1
  /**
2
2
  * Identity Manager
3
3
  *
4
- * Handles user identification, property management, and alias creation.
5
- * Provides high-level identity operations that coordinate between
6
- * UserManager and event capture.
4
+ * Orchestrates user identification, property updates, and alias creation.
5
+ * All state lives in UserManager; this layer owns validation, dedup,
6
+ * event capture, and lifecycle events.
7
7
  */
8
- import { EventPayload } from "../types";
8
+ import type { EventPayload, IdentityUpdate } from "../types";
9
9
  /**
10
- * Interface for the VTilt instance methods needed by IdentityManager.
11
- * Using a minimal interface avoids circular dependency.
10
+ * Minimal interface for the VTilt host.
11
+ * Avoids circular dependency while giving IdentityManager access to
12
+ * capture, user state reads, batched writes, session reset, and events.
12
13
  */
13
14
  export interface IdentityHost {
14
15
  capture(name: string, payload: EventPayload): void;
15
16
  userManager: {
16
17
  getDistinctId(): string | null;
17
- setDistinctId(id: string): void;
18
18
  getAnonymousId(): string;
19
19
  getDeviceId(): string;
20
- ensureDeviceId(id: string): void;
21
20
  getUserState(): "anonymous" | "identified";
22
- setUserState(state: "anonymous" | "identified"): void;
23
21
  getUserIdentity(): Record<string, any>;
24
22
  getUserProperties(): Record<string, any>;
25
- updateUserProperties(set?: Record<string, any>, setOnce?: Record<string, any>): void;
26
- setUserProperties(set?: Record<string, any>, setOnce?: Record<string, any>): boolean;
27
- isDistinctIdStringLikePublic(id: string): boolean;
28
- createAlias(alias: string, original?: string): {
29
- distinct_id: string;
30
- original: string;
31
- } | null;
23
+ applyUpdate(update: IdentityUpdate): void;
32
24
  reset(resetDeviceId?: boolean): void;
33
25
  };
34
26
  sessionManager: {
35
27
  resetSessionId(): void;
36
28
  };
37
29
  _emitter?: {
38
- emit(event: string, payload?: any): void;
30
+ emit(event: string, payload?: unknown): void;
39
31
  };
40
32
  }
41
33
  export declare class IdentityManager {
42
34
  private _host;
35
+ private _lastIdentifyHash;
36
+ private _lastSetPropertiesHash;
43
37
  constructor(host: IdentityHost);
44
38
  /**
45
- * Identify a user with property operations.
46
- * Sends $identify event when transitioning from anonymous to identified.
39
+ * Identify a user.
47
40
  *
48
- * @param newDistinctId - User's unique identifier
49
- * @param userPropertiesToSet - Properties to set (can be updated)
50
- * @param userPropertiesToSetOnce - Properties to set once (preserves first value)
41
+ * - Anonymous identified: sends $identify (merge), persists after capture.
42
+ * - Already identified, different user: sends $identify (re-identify).
43
+ * - Same user, new properties only: delegates to setUserProperties.
44
+ * - Identical consecutive calls are deduplicated (React re-render safety).
51
45
  */
52
46
  identify(newDistinctId?: string, userPropertiesToSet?: Record<string, any>, userPropertiesToSetOnce?: Record<string, any>): void;
53
47
  /**
54
48
  * Set user properties without changing identity.
49
+ * Dedup mirrors identify: identical consecutive calls are skipped.
55
50
  */
56
51
  setUserProperties(userPropertiesToSet?: Record<string, any>, userPropertiesToSetOnce?: Record<string, any>): void;
57
52
  /**
58
- * Reset user identity (logout).
59
- * Clears all user data, generates new anonymous ID, resets session.
60
- *
61
- * @param reset_device_id - If true, also resets device_id
53
+ * Reset identity (logout).
54
+ * Clears user data, generates new anonymous ID, resets session.
62
55
  */
63
- resetUser(reset_device_id?: boolean): void;
56
+ resetUser(resetDeviceId?: boolean): void;
64
57
  /**
65
- * Create an alias to link two distinct IDs.
66
- * Links anonymous session to account on signup.
67
- *
68
- * @param alias - A unique identifier that you want to use for this user
69
- * @param original - The current identifier being used (optional)
58
+ * Create an alias linking two distinct IDs.
59
+ * If alias matches current identity, falls back to identify().
70
60
  */
71
- createAlias(alias: string, original?: string): void;
61
+ alias(alias: string, original?: string): void;
72
62
  getUserIdentity(): Record<string, any>;
73
63
  getDeviceId(): string;
74
64
  getUserState(): "anonymous" | "identified";
@@ -10,4 +10,10 @@
10
10
  export { RemoteConfigManager, type RemoteConfigHost, type RemoteConfigCallback, } from "./remote-config";
11
11
  export { CaptureManager, type CaptureHost, type CaptureOptions, } from "./capture";
12
12
  export { IdentityManager, type IdentityHost } from "./identity";
13
- export { FeatureManager, createFeatureManager, type FeatureHost, type FeatureEntry, type FeatureClass, } from "./feature-manager";
13
+ export { ConsentManager, type ConsentHost, type ConsentState } from "./consent";
14
+ export { initFacebookCookies, removeFacebookCookies, getFbp, getFbc, } from "./fb-cookies";
15
+ export { FeatureManager, type FeatureHost, type FeatureDescriptor, } from "./feature-manager";
16
+ export { isPageleaveEnabled } from "./config-utils";
17
+ export { PageLifecycleTracker } from "./page-lifecycle";
18
+ export { EventBuffer, type EventBufferHost, type BufferedEvent, } from "./event-buffer";
19
+ export { FifoQueue } from "./fifo-queue";
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Page context, dwell time, and inter-event engagement for GA4-style metrics.
3
+ */
4
+ import type { EventPayload } from "../types";
5
+ import type { VTiltConfig } from "../types";
6
+ export interface PageleaveCaptureFn {
7
+ (name: string, payload: EventPayload, options?: {
8
+ skip_engagement?: boolean;
9
+ }): void;
10
+ }
11
+ /**
12
+ * Tracks time on current URL and milliseconds since last capture for engagement_time_msec.
13
+ */
14
+ export declare class PageLifecycleTracker {
15
+ private _pageEnterPerf;
16
+ /** Null until first engagement-consuming event; then wall clock for next delta */
17
+ private _lastCapturePerf;
18
+ private readonly _bootPerf;
19
+ private _prevUrl;
20
+ private _prevPathname;
21
+ private _prevTitle;
22
+ /** Dedupe pagehide + visibility hidden for the same page */
23
+ private _leaveEmittedForPageKey;
24
+ /**
25
+ * Call after a $pageview is sent (initial or SPA) to reset dwell timer and snapshot.
26
+ */
27
+ onAfterPageview(): void;
28
+ /**
29
+ * Milliseconds since last capture (or since boot on the first event). Does not advance the clock.
30
+ */
31
+ peekEngagementSinceLastCaptureMs(): number;
32
+ /** Call after a capture is enqueued so the next peek measures from this point. */
33
+ commitCaptureClock(): void;
34
+ private _pageKey;
35
+ /** Call when the tab becomes visible again so a later hide can emit another $pageleave. */
36
+ clearLeaveDedupe(): void;
37
+ /**
38
+ * Fire $pageleave if enabled and not already emitted for this page.
39
+ */
40
+ tryCapturePageleave(config: VTiltConfig, capture: PageleaveCaptureFn, reason: string, extraProps?: EventPayload): void;
41
+ }