@v-tilt/browser 1.10.10 → 1.12.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.
- package/dist/all-external-dependencies.js +1 -1
- package/dist/all-external-dependencies.js.map +1 -1
- package/dist/array.full.js +1 -1
- package/dist/array.full.js.map +1 -1
- package/dist/array.js +1 -1
- package/dist/array.js.map +1 -1
- package/dist/array.no-external.js +1 -1
- package/dist/array.no-external.js.map +1 -1
- package/dist/autocapture-types.d.ts +17 -0
- package/dist/autocapture-utils.d.ts +24 -1
- package/dist/autocapture.d.ts +94 -5
- package/dist/chat.js +1 -1
- package/dist/chat.js.map +1 -1
- package/dist/config.d.ts +8 -1
- package/dist/constants.d.ts +19 -13
- package/dist/core/capture.d.ts +15 -5
- package/dist/core/config-utils.d.ts +15 -0
- package/dist/core/consent.d.ts +62 -0
- package/dist/core/event-buffer.d.ts +60 -0
- package/dist/core/fb-cookies.d.ts +32 -0
- package/dist/core/feature-manager.d.ts +61 -69
- package/dist/core/fifo-queue.d.ts +23 -0
- package/dist/core/identity.d.ts +23 -33
- package/dist/core/index.d.ts +7 -1
- package/dist/core/page-lifecycle.d.ts +41 -0
- package/dist/core/remote-config.d.ts +14 -17
- package/dist/extensions/chat/bubble-drag.d.ts +30 -0
- package/dist/extensions/chat/chat-api.d.ts +15 -0
- package/dist/extensions/chat/chat-styles.d.ts +27 -0
- package/dist/extensions/chat/chat-wrapper.d.ts +20 -145
- package/dist/extensions/chat/chat.d.ts +129 -12
- package/dist/extensions/chat/message-content-styles.d.ts +1 -0
- package/dist/extensions/chat/message-html.d.ts +6 -0
- package/dist/extensions/chat/message-markdown.d.ts +8 -0
- package/dist/extensions/chat/normalize-send-content.d.ts +24 -0
- package/dist/extensions/chat/types.d.ts +19 -57
- package/dist/extensions/chat/widget-registry.d.ts +53 -0
- package/dist/extensions/chat/widgets/collect-email.d.ts +6 -0
- package/dist/extensions/chat/widgets/escalate-to-human.d.ts +6 -0
- package/dist/extensions/ga4-proxy.d.ts +59 -0
- package/dist/extensions/google-tag-gateway/consent-bridge.d.ts +27 -0
- package/dist/extensions/google-tag-gateway/enhanced-conversions.d.ts +35 -0
- package/dist/extensions/google-tag-gateway/event-bridge.d.ts +74 -0
- package/dist/extensions/google-tag-gateway/google-tag-gateway.d.ts +95 -0
- package/dist/extensions/google-tag-gateway/gtag-loader.d.ts +85 -0
- package/dist/extensions/google-tag-gateway/index.d.ts +7 -0
- package/dist/extensions/google-tag-gateway/normalize.d.ts +28 -0
- package/dist/extensions/google-tag-gateway/public-api.d.ts +23 -0
- package/dist/extensions/history-autocapture.d.ts +2 -2
- package/dist/extensions/replay/index.d.ts +1 -1
- package/dist/extensions/replay/session-recording-utils.d.ts +13 -43
- package/dist/extensions/replay/session-recording-wrapper.d.ts +10 -66
- package/dist/extensions/replay/session-recording.d.ts +53 -1
- package/dist/extensions/replay/types.d.ts +6 -1
- package/dist/extensions/web-vitals/web-vitals-manager.d.ts +14 -43
- package/dist/external-scripts-loader.js +1 -1
- package/dist/external-scripts-loader.js.map +1 -1
- package/dist/feature.d.ts +54 -172
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.d.ts +728 -753
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +728 -753
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/rate-limiter.d.ts +0 -1
- package/dist/recorder.js +1 -1
- package/dist/recorder.js.map +1 -1
- package/dist/request.d.ts +34 -20
- package/dist/scroll-depth-tracker.d.ts +42 -0
- package/dist/server.d.ts +114 -0
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +12 -0
- package/dist/types.d.ts +204 -9
- package/dist/user-manager.d.ts +26 -52
- package/dist/utils/base64.d.ts +30 -0
- package/dist/utils/bot-detection.d.ts +28 -0
- package/dist/utils/endpoint-url.d.ts +36 -0
- package/dist/utils/event-emitter.d.ts +1 -0
- package/dist/utils/globals.d.ts +71 -2
- package/dist/utils/index.d.ts +20 -5
- package/dist/utils/logger.d.ts +66 -0
- package/dist/utils/request-utils.d.ts +5 -0
- package/dist/utils/safewrap.d.ts +6 -1
- package/dist/utils/transport-health.d.ts +55 -0
- package/dist/utils/type-guards.d.ts +1 -1
- package/dist/vtilt.d.ts +85 -25
- package/dist/web-vitals.js.map +1 -1
- package/package.json +9 -4
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Type definitions for the chat widget extension.
|
|
5
5
|
* Core types are re-exported from globals.ts for consistency.
|
|
6
6
|
*/
|
|
7
|
-
export type { ChatMessage, ChatChannel, ChatChannelSummary, ChatConfig, ChatTheme, ChatWidgetView, LazyLoadedChatInterface, } from "../../utils/globals";
|
|
7
|
+
export type { ChatMessage, ChatChannel, ChatChannelSummary, ChatConfig, ChatTheme, ChatWidgetView, LazyLoadedChatInterface, SendChatMessageContent, SendChatMessageOptions, } from "../../utils/globals";
|
|
8
|
+
export { messageUsesRichRendering, normalizeSendChatMessageContent, } from "./normalize-send-content";
|
|
9
|
+
export type { NormalizedOutgoingMessage } from "./normalize-send-content";
|
|
8
10
|
/**
|
|
9
11
|
* Sender types for chat messages
|
|
10
12
|
*/
|
|
@@ -45,75 +47,35 @@ export interface PresenceStatus {
|
|
|
45
47
|
current_page_url?: string;
|
|
46
48
|
}
|
|
47
49
|
/**
|
|
48
|
-
*
|
|
50
|
+
* Normalized channel event names for analytics tracking
|
|
49
51
|
*
|
|
50
52
|
* Message events are tracked client-side via capture() for full enrichment
|
|
51
|
-
* (
|
|
53
|
+
* (session, page URL, device, person properties, etc.).
|
|
52
54
|
* Widget UI events (open/close) are exposed via callbacks in ChatConfig.
|
|
53
55
|
*
|
|
54
|
-
* All
|
|
55
|
-
* - $
|
|
56
|
-
* - $
|
|
57
|
-
* - $message_id:
|
|
56
|
+
* All channel message events use $channel_message with normalized properties:
|
|
57
|
+
* - $channel_type: channel discriminator ('chat', 'email', 'sms')
|
|
58
|
+
* - $conversation_id: conversation identifier
|
|
59
|
+
* - $message_id: message identifier
|
|
60
|
+
* - $direction: 'inbound' | 'outbound'
|
|
61
|
+
* - $sender_type: 'user' | 'ai' | 'agent' | 'system'
|
|
62
|
+
* - $content_preview: first 100 chars of content
|
|
58
63
|
*
|
|
59
64
|
* Tracking is gated by remote config:
|
|
60
65
|
* - chatTracking.trackUserMessages → captures when $sender_type=user
|
|
61
66
|
* - chatTracking.trackAgentMessages → captures when $sender_type=ai|agent
|
|
62
67
|
*/
|
|
63
|
-
export declare const
|
|
64
|
-
|
|
65
|
-
readonly MESSAGE: "$chat_message";
|
|
68
|
+
export declare const CHANNEL_EVENTS: {
|
|
69
|
+
readonly MESSAGE: "$channel_message";
|
|
66
70
|
};
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
export interface ChatMessageEventProperties {
|
|
71
|
-
$channel_id: string;
|
|
72
|
-
$message_id: string;
|
|
73
|
-
$sender_type: SenderType;
|
|
74
|
-
$content_preview: string;
|
|
75
|
-
$ai_mode: boolean;
|
|
76
|
-
$word_count: number;
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* @deprecated Use ChatMessageEventProperties instead
|
|
80
|
-
*/
|
|
81
|
-
export interface ChatMessageSentEventProperties {
|
|
82
|
-
$channel_id: string;
|
|
71
|
+
export interface ChannelMessageEventProperties {
|
|
72
|
+
$channel_type: string;
|
|
73
|
+
$conversation_id: string;
|
|
83
74
|
$message_id: string;
|
|
84
|
-
$
|
|
75
|
+
$direction: "inbound" | "outbound";
|
|
85
76
|
$sender_type: SenderType;
|
|
86
|
-
$ai_mode: boolean;
|
|
87
|
-
$word_count: number;
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* @deprecated Use ChatMessageEventProperties instead
|
|
91
|
-
*/
|
|
92
|
-
export interface ChatMessageReceivedEventProperties {
|
|
93
|
-
$channel_id: string;
|
|
94
|
-
$message_id: string;
|
|
95
77
|
$content_preview: string;
|
|
96
|
-
$
|
|
97
|
-
$response_time_ms?: number;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Chat event properties for $chat_started
|
|
101
|
-
*/
|
|
102
|
-
export interface ChatStartedEventProperties {
|
|
103
|
-
$channel_id: string;
|
|
104
|
-
$initiated_by: "user" | "agent";
|
|
105
|
-
$ai_mode: boolean;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Chat event properties for $chat_closed
|
|
109
|
-
*/
|
|
110
|
-
export interface ChatClosedEventProperties {
|
|
111
|
-
$channel_id: string;
|
|
112
|
-
$resolved_by: "agent" | "user" | "system" | "auto";
|
|
113
|
-
$resolution: "resolved" | "unresolved" | "abandoned";
|
|
114
|
-
$message_count: number;
|
|
115
|
-
$duration_seconds: number;
|
|
116
|
-
$ai_only: boolean;
|
|
78
|
+
$ai_mode?: boolean;
|
|
117
79
|
}
|
|
118
80
|
/**
|
|
119
81
|
* Internal widget state
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chat Widget Registry - Extensible widget system for AI-triggered UI.
|
|
3
|
+
* Tool calls from the AI are mapped to registered widgets; each widget provides render() and onAction().
|
|
4
|
+
*/
|
|
5
|
+
export interface WidgetContext {
|
|
6
|
+
channelId: string;
|
|
7
|
+
distinctId: string;
|
|
8
|
+
primaryColor: string;
|
|
9
|
+
/** @deprecated Use buildEndpointUrl() instead */
|
|
10
|
+
apiBase: string;
|
|
11
|
+
token: string;
|
|
12
|
+
/** The ID of the message containing this widget */
|
|
13
|
+
messageId: string;
|
|
14
|
+
/** Build a full URL for a given endpoint path, respecting api_host */
|
|
15
|
+
buildEndpointUrl(path: string): string;
|
|
16
|
+
}
|
|
17
|
+
export interface WidgetActionResult {
|
|
18
|
+
/** Whether the action succeeded — drives metadata-based re-render */
|
|
19
|
+
success?: boolean;
|
|
20
|
+
/** @deprecated Use metadata-driven rendering instead. Kept for custom widget compat. */
|
|
21
|
+
replaceHTML?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface ChatWidgetDefinition {
|
|
24
|
+
/** Unique type identifier, e.g. "collect_email", "schedule_meeting" */
|
|
25
|
+
type: string;
|
|
26
|
+
/** AI tool description (sent to LLM so it knows when to invoke) */
|
|
27
|
+
toolDescription: string;
|
|
28
|
+
/** JSON schema shape for the tool parameters */
|
|
29
|
+
parameters: Record<string, {
|
|
30
|
+
type: string;
|
|
31
|
+
description: string;
|
|
32
|
+
}>;
|
|
33
|
+
/** Returns an HTML string to render inline in the chat bubble */
|
|
34
|
+
render(params: Record<string, unknown>, context: WidgetContext): string;
|
|
35
|
+
/** Called when the widget form is submitted or button clicked */
|
|
36
|
+
onAction(action: string, data: Record<string, unknown>, context: WidgetContext): Promise<WidgetActionResult>;
|
|
37
|
+
}
|
|
38
|
+
export declare class ChatWidgetRegistry {
|
|
39
|
+
private _widgets;
|
|
40
|
+
register(definition: ChatWidgetDefinition): void;
|
|
41
|
+
get(type: string): ChatWidgetDefinition | undefined;
|
|
42
|
+
getAll(): ChatWidgetDefinition[];
|
|
43
|
+
/**
|
|
44
|
+
* Build tool definitions for the AI from all registered widgets.
|
|
45
|
+
*/
|
|
46
|
+
getToolDefinitions(): Record<string, {
|
|
47
|
+
description: string;
|
|
48
|
+
parameters: Record<string, {
|
|
49
|
+
type: string;
|
|
50
|
+
description: string;
|
|
51
|
+
}>;
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in collect_email widget: renders name + email inputs, POSTs to widget actions API.
|
|
3
|
+
* UI state is metadata-driven — submitted state is rendered by _renderWidgets().
|
|
4
|
+
*/
|
|
5
|
+
import type { ChatWidgetDefinition } from "../widget-registry";
|
|
6
|
+
export declare const collectEmailWidget: ChatWidgetDefinition;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in escalate_to_human widget: shows a "Connect with a human" button.
|
|
3
|
+
* UI state is metadata-driven — submitted state is rendered by _renderWidgets().
|
|
4
|
+
*/
|
|
5
|
+
import type { ChatWidgetDefinition } from "../widget-registry";
|
|
6
|
+
export declare const escalateToHumanWidget: ChatWidgetDefinition;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GA4 Proxy Feature
|
|
3
|
+
*
|
|
4
|
+
* Automatically configures gtag.js to send through vTilt's domain.
|
|
5
|
+
* When enabled, either reconfigures an existing gtag.js or loads a new one
|
|
6
|
+
* with server_container_url pointing to vTilt's proxy routes.
|
|
7
|
+
*
|
|
8
|
+
* Config sources (init config takes priority over remote config):
|
|
9
|
+
* vt.init({ ga4: { measurementId: 'G-XXX' } })
|
|
10
|
+
* /api/decide → { ga4: { measurementId: 'G-XXX' } }
|
|
11
|
+
*/
|
|
12
|
+
import type { VTiltConfig } from "../types";
|
|
13
|
+
import type { Feature, FeatureConfig } from "../feature";
|
|
14
|
+
export interface GA4ProxyConfig extends FeatureConfig {
|
|
15
|
+
measurementId?: string;
|
|
16
|
+
debugMode?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare class GA4Proxy implements Feature {
|
|
19
|
+
readonly name = "GA4Proxy";
|
|
20
|
+
private _instance;
|
|
21
|
+
private _config;
|
|
22
|
+
private _isStarted;
|
|
23
|
+
private _pendingGtagCallbacks;
|
|
24
|
+
/** Fires once both client_id and session_id gtag callbacks complete. */
|
|
25
|
+
onIdentityResolved?: () => void;
|
|
26
|
+
constructor(instance: {
|
|
27
|
+
getConfig(): VTiltConfig;
|
|
28
|
+
}, config?: GA4ProxyConfig);
|
|
29
|
+
static extractConfig(config: VTiltConfig): GA4ProxyConfig;
|
|
30
|
+
get isEnabled(): boolean;
|
|
31
|
+
get isStarted(): boolean;
|
|
32
|
+
startIfEnabled(): void;
|
|
33
|
+
stop(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Sync user_id to gtag.js so both proxy and MP event streams share
|
|
36
|
+
* the same identity after identify(). GA4 Realtime uses user_id as
|
|
37
|
+
* the highest-priority identifier; without this, proxy events (no
|
|
38
|
+
* user_id) and MP events (with user_id) count as separate users.
|
|
39
|
+
*/
|
|
40
|
+
setUserId(userId: string | null): void;
|
|
41
|
+
onConfigUpdate(config: VTiltConfig): void;
|
|
42
|
+
private _start;
|
|
43
|
+
private _getApiBase;
|
|
44
|
+
private _hasGtag;
|
|
45
|
+
private _reconfigureExistingGtag;
|
|
46
|
+
private _loadAndConfigureGtag;
|
|
47
|
+
/**
|
|
48
|
+
* Use gtag('get', ...) to obtain the authoritative client_id and
|
|
49
|
+
* session_id from gtag.js. Fires onIdentityResolved once BOTH
|
|
50
|
+
* callbacks have completed (counter-based).
|
|
51
|
+
*/
|
|
52
|
+
private _resolveGtagIdentity;
|
|
53
|
+
/**
|
|
54
|
+
* Fires after each gtag('get', ...) callback. When all pending callbacks
|
|
55
|
+
* have settled, notifies the host (EventBuffer) so buffered MP events
|
|
56
|
+
* can flush with stable GA4 IDs (same client_id as gtag.js).
|
|
57
|
+
*/
|
|
58
|
+
private _onGtagCallbackSettled;
|
|
59
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Consent Mode v2 bridge.
|
|
3
|
+
*
|
|
4
|
+
* Maps vTilt's 3-boolean consent (analytics / marketing / advertising) to
|
|
5
|
+
* Google's 4-key Consent Mode v2 payload (ad_storage, ad_user_data,
|
|
6
|
+
* ad_personalization, analytics_storage) and pushes it through gtag.
|
|
7
|
+
*
|
|
8
|
+
* Mapping (default):
|
|
9
|
+
* advertising -> ad_storage + ad_user_data + ad_personalization
|
|
10
|
+
* analytics -> analytics_storage
|
|
11
|
+
* marketing is not sent to Google by default (it covers email/SMS, not ads).
|
|
12
|
+
*
|
|
13
|
+
* Admin UI may override individual keys (`consentOverride`) to hard-wire a
|
|
14
|
+
* specific value regardless of user consent — useful for jurisdictions or
|
|
15
|
+
* projects with fixed policies.
|
|
16
|
+
*/
|
|
17
|
+
import type { ConsentState } from "../../core/consent";
|
|
18
|
+
import type { GoogleConsentOverride } from "../../types";
|
|
19
|
+
export type GtagFn = (...args: unknown[]) => void;
|
|
20
|
+
export type GoogleConsentPayload = Record<"ad_storage" | "ad_user_data" | "ad_personalization" | "analytics_storage", "granted" | "denied">;
|
|
21
|
+
export declare function buildConsentPayload(consent: ConsentState, override?: GoogleConsentOverride): GoogleConsentPayload;
|
|
22
|
+
/**
|
|
23
|
+
* Send the default consent state BEFORE gtag.js loads so that every
|
|
24
|
+
* subsequent request carries the right flags. Call once at feature start.
|
|
25
|
+
*/
|
|
26
|
+
export declare function pushConsentDefault(gtag: GtagFn, consent: ConsentState, override?: GoogleConsentOverride): void;
|
|
27
|
+
export declare function pushConsentUpdate(gtag: GtagFn, consent: ConsentState, override?: GoogleConsentOverride): void;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Enhanced Conversions helper.
|
|
3
|
+
*
|
|
4
|
+
* Hashes user-provided PII (email/phone/name/address) with SHA-256 before
|
|
5
|
+
* it leaves the browser, matching Google Ads' Enhanced Conversions format:
|
|
6
|
+
* https://support.google.com/google-ads/answer/13258081
|
|
7
|
+
*
|
|
8
|
+
* Returns an object suitable for `gtag('set', 'user_data', { ... })`.
|
|
9
|
+
*/
|
|
10
|
+
export interface RawUserData {
|
|
11
|
+
email?: string | null;
|
|
12
|
+
phone?: string | null;
|
|
13
|
+
first_name?: string | null;
|
|
14
|
+
last_name?: string | null;
|
|
15
|
+
street?: string | null;
|
|
16
|
+
city?: string | null;
|
|
17
|
+
region?: string | null;
|
|
18
|
+
postal_code?: string | null;
|
|
19
|
+
country?: string | null;
|
|
20
|
+
}
|
|
21
|
+
export interface HashedUserAddress {
|
|
22
|
+
sha256_first_name?: string;
|
|
23
|
+
sha256_last_name?: string;
|
|
24
|
+
sha256_street?: string;
|
|
25
|
+
city?: string;
|
|
26
|
+
region?: string;
|
|
27
|
+
postal_code?: string;
|
|
28
|
+
country?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface HashedUserData {
|
|
31
|
+
sha256_email_address?: string;
|
|
32
|
+
sha256_phone_number?: string;
|
|
33
|
+
address?: HashedUserAddress;
|
|
34
|
+
}
|
|
35
|
+
export declare function buildHashedUserData(raw: RawUserData): Promise<HashedUserData | null>;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Automatic event bridge from `vt.capture()` into `gtag('event', ...)`.
|
|
3
|
+
*
|
|
4
|
+
* Two layers run in order on every captured event:
|
|
5
|
+
*
|
|
6
|
+
* 1. **GA4 forwarder** (`forwardToGtag`) — applies the admin's
|
|
7
|
+
* `event_filter`, then an explicit `event_mappings` row, then a built-in
|
|
8
|
+
* preset (e.g. `$pageview` → `page_view`), then the `autoForward`
|
|
9
|
+
* default-on fallback. Fires `gtag('event', mapped_name, params)` on
|
|
10
|
+
* success. This is the primary path: because most customers link GA4 to
|
|
11
|
+
* Google Ads, the GA4 event flows to Ads automatically when marked as a
|
|
12
|
+
* conversion.
|
|
13
|
+
* 2. **Ads-only conversions** (`forwardEvent`) — for customers who
|
|
14
|
+
* configured direct Ads conversion mappings on the destination. Fires
|
|
15
|
+
* `gtag('event', 'conversion', {send_to: 'AW-.../...'})`. Unchanged by
|
|
16
|
+
* design so existing deployments keep working.
|
|
17
|
+
*
|
|
18
|
+
* Matching on both layers is by exact event name. vTilt's own internal
|
|
19
|
+
* events (`$identify`, `$set`, etc.) are skipped unless the admin wired
|
|
20
|
+
* them up explicitly.
|
|
21
|
+
*/
|
|
22
|
+
import type { GoogleAdsConversionMapping, GoogleTagClientConfig } from "../../types";
|
|
23
|
+
import type { GtagFn } from "./consent-bridge";
|
|
24
|
+
export interface ConversionBuildOptions {
|
|
25
|
+
mapping: GoogleAdsConversionMapping;
|
|
26
|
+
payload: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
export interface BuiltConversion {
|
|
29
|
+
send_to: string;
|
|
30
|
+
params: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
export declare function buildConversionParams({ mapping, payload, }: ConversionBuildOptions): BuiltConversion;
|
|
33
|
+
/**
|
|
34
|
+
* Bridge a single captured event to gtag using the Ads conversion mappings
|
|
35
|
+
* that match `eventName`. Each matching mapping results in one
|
|
36
|
+
* `gtag('event', 'conversion', ...)` call.
|
|
37
|
+
*/
|
|
38
|
+
export declare function forwardEvent(gtag: GtagFn, eventName: string, payload: Record<string, unknown>, config: GoogleTagClientConfig): BuiltConversion[];
|
|
39
|
+
/**
|
|
40
|
+
* Check whether a captured event name is allowed by the admin's
|
|
41
|
+
* `event_filter`. `exclude` wins over `include`; an empty/missing
|
|
42
|
+
* `include` means "all events pass". Mirrors the server-side filter
|
|
43
|
+
* semantics exactly.
|
|
44
|
+
*/
|
|
45
|
+
export declare function isEventAllowed(eventName: string, filter: GoogleTagClientConfig["eventFilter"]): boolean;
|
|
46
|
+
export interface ForwardToGtagResult {
|
|
47
|
+
/** `true` when a `gtag('event', ...)` call was made. */
|
|
48
|
+
fired: boolean;
|
|
49
|
+
/** The event name that was forwarded (post-mapping/preset), if fired. */
|
|
50
|
+
eventName?: string;
|
|
51
|
+
/** The params object that was passed to gtag, if fired. */
|
|
52
|
+
params?: Record<string, unknown>;
|
|
53
|
+
/** Which rule produced the forward. */
|
|
54
|
+
source?: "mapping" | "preset" | "auto";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Forward one captured event to gtag as a GA4 event. Resolution order:
|
|
58
|
+
*
|
|
59
|
+
* 1. Filter — `event_filter.exclude` drops, then `event_filter.include`
|
|
60
|
+
* (when non-empty) whitelists. Same as the server-side filter.
|
|
61
|
+
* 2. Explicit mapping — an `event_mappings` row with matching `source`
|
|
62
|
+
* wins and always fires, regardless of `autoForward`. This is the
|
|
63
|
+
* admin's "force fire" path.
|
|
64
|
+
* 3. Built-in preset — when no explicit mapping is set and
|
|
65
|
+
* `autoForward !== false`, `$pageview`/`$pageleave` fire their GA4
|
|
66
|
+
* analogues with sensibly-named params.
|
|
67
|
+
* 4. Auto-forward fallback — when `autoForward !== false` and the event
|
|
68
|
+
* is not a `$*` internal event, fire `gtag('event', eventName, payload)`
|
|
69
|
+
* with reserved/internal keys stripped.
|
|
70
|
+
*
|
|
71
|
+
* Admins who want a strict allowlist set `autoForward: false` and use
|
|
72
|
+
* `event_mappings` to enumerate the events they care about.
|
|
73
|
+
*/
|
|
74
|
+
export declare function forwardToGtag(gtag: GtagFn, eventName: string, payload: Record<string, unknown>, config: GoogleTagClientConfig): ForwardToGtagResult;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GoogleTagGateway feature.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates the client-side half of the Google Tag Gateway destination:
|
|
5
|
+
*
|
|
6
|
+
* 1. Maintains `window.dataLayer` / `window.gtag`.
|
|
7
|
+
* 2. Pushes Consent Mode v2 defaults BEFORE gtag.js loads, and re-pushes
|
|
8
|
+
* on `consent:updated` events.
|
|
9
|
+
* 3. Injects `gtag/js?id=<primary>` from the vTilt gateway (`/gt`) so
|
|
10
|
+
* every measurement request flows through the first-party origin.
|
|
11
|
+
* 4. Subscribes to EVENT_CAPTURED and forwards mapped events to Ads
|
|
12
|
+
* (`gtag('event', 'conversion', ...)`).
|
|
13
|
+
* 5. Exposes a flat `vt.gtag(...)` passthrough for power users.
|
|
14
|
+
*
|
|
15
|
+
* Forwards only after `__remote_config_loaded` is true (same signal as
|
|
16
|
+
* EventBuffer: fresh `/decide` applied, bootstrap, or fetch failure).
|
|
17
|
+
* Captures before that are queued so we never drop events just because
|
|
18
|
+
* `/decide` has not committed yet. After remote is ready, we install the
|
|
19
|
+
* official `dataLayer` + `gtag` shim (`ensureDataLayer`) and forward
|
|
20
|
+
* immediately — the shim queues until `gtag.js` loads; we do not maintain a
|
|
21
|
+
* second SDK queue for that window.
|
|
22
|
+
*
|
|
23
|
+
* Feature lifecycle matches the rest of the SDK — registered with
|
|
24
|
+
* FeatureManager via a descriptor that pulls the admin-configured shape
|
|
25
|
+
* out of `/decide` under the `googleTag` key.
|
|
26
|
+
*/
|
|
27
|
+
import type { VTilt } from "../../vtilt";
|
|
28
|
+
import type { VTiltConfig, GoogleTagClientConfig } from "../../types";
|
|
29
|
+
import type { Feature, FeatureConfig } from "../../feature";
|
|
30
|
+
import { type GtagFn } from "./consent-bridge";
|
|
31
|
+
import { type GtagCall } from "./public-api";
|
|
32
|
+
import { type RawUserData } from "./enhanced-conversions";
|
|
33
|
+
export interface GoogleTagGatewayFeatureConfig extends FeatureConfig {
|
|
34
|
+
remote?: GoogleTagClientConfig;
|
|
35
|
+
}
|
|
36
|
+
export interface DeliveryLogEntry {
|
|
37
|
+
ts: number;
|
|
38
|
+
tag_ids: string[];
|
|
39
|
+
event_name: string;
|
|
40
|
+
send_to: string;
|
|
41
|
+
status: "fired" | "dropped";
|
|
42
|
+
reason?: string;
|
|
43
|
+
}
|
|
44
|
+
export declare class GoogleTagGateway implements Feature {
|
|
45
|
+
readonly name = "GoogleTagGateway";
|
|
46
|
+
private _instance;
|
|
47
|
+
private _config;
|
|
48
|
+
private _isStarted;
|
|
49
|
+
private _scriptInjected;
|
|
50
|
+
private _scriptLoaded;
|
|
51
|
+
private _consentDefaultsPushed;
|
|
52
|
+
private _gtag;
|
|
53
|
+
private _publicApi;
|
|
54
|
+
private _loaderOptions;
|
|
55
|
+
private _unsubscribeCaptured;
|
|
56
|
+
private _unsubscribeConsent;
|
|
57
|
+
private _deliveryLog;
|
|
58
|
+
private readonly _maxDeliveryLog;
|
|
59
|
+
/**
|
|
60
|
+
* Captures observed before `__remote_config_loaded` only. Once remote
|
|
61
|
+
* commits, `ensureDataLayer` + gtag forward — gtag's own queue handles
|
|
62
|
+
* ordering until `gtag.js` is on the wire.
|
|
63
|
+
*/
|
|
64
|
+
private readonly _pendingCaptures;
|
|
65
|
+
constructor(instance: VTilt, config?: GoogleTagGatewayFeatureConfig);
|
|
66
|
+
static extractConfig(config: VTiltConfig): GoogleTagGatewayFeatureConfig;
|
|
67
|
+
get isEnabled(): boolean;
|
|
68
|
+
get isStarted(): boolean;
|
|
69
|
+
/** Exposed for tests and the public `vt.gtag` binding. */
|
|
70
|
+
get gtag(): GtagFn;
|
|
71
|
+
get deliveryLog(): DeliveryLogEntry[];
|
|
72
|
+
startIfEnabled(): void;
|
|
73
|
+
stop(): void;
|
|
74
|
+
onConfigUpdate(config: VTiltConfig): void;
|
|
75
|
+
/**
|
|
76
|
+
* Set the user identifiers that power Enhanced Conversions. Values are
|
|
77
|
+
* normalized + SHA-256 hashed client-side before being pushed to gtag,
|
|
78
|
+
* so raw PII never leaves the browser.
|
|
79
|
+
*/
|
|
80
|
+
setUserData(raw: RawUserData): Promise<void>;
|
|
81
|
+
getRecentPublicCalls(): GtagCall[];
|
|
82
|
+
private _start;
|
|
83
|
+
private _drainPending;
|
|
84
|
+
private _pushConsentDefaultsOnce;
|
|
85
|
+
private _subscribeCaptured;
|
|
86
|
+
/**
|
|
87
|
+
* Handle one captured event. Only `__remote_config_loaded === false` uses
|
|
88
|
+
* `_pendingCaptures`. After remote commits, we boot the gtag pipeline on
|
|
89
|
+
* demand (`startIfEnabled`) and forward — the dataLayer shim buffers until
|
|
90
|
+
* `gtag.js` loads.
|
|
91
|
+
*/
|
|
92
|
+
private _handleCaptured;
|
|
93
|
+
private _subscribeConsent;
|
|
94
|
+
private _record;
|
|
95
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gtag.js loader.
|
|
3
|
+
*
|
|
4
|
+
* Loads `gtag/js?id=<primary>` from the vTilt gateway (`/gt`) so that every
|
|
5
|
+
* subsequent measurement request — `gtag/js`, `/g/collect`, and
|
|
6
|
+
* `/pagead/conversion` — stays on the first-party origin. Installs the
|
|
7
|
+
* standard `window.dataLayer` queue and `window.gtag` shim exactly the way
|
|
8
|
+
* Google's official snippet does, so any third-party integration that
|
|
9
|
+
* expects them (Ads editor, Tag Assistant, CMP libraries) keeps working.
|
|
10
|
+
*/
|
|
11
|
+
import type { GoogleTagClientConfig } from "../../types";
|
|
12
|
+
import type { GtagFn } from "./consent-bridge";
|
|
13
|
+
export interface GtagLoaderOptions {
|
|
14
|
+
/** Origin to load gtag.js from. Must include protocol. */
|
|
15
|
+
origin: string;
|
|
16
|
+
/** Path under `origin` that hosts the gateway. Defaults to `/gt`. */
|
|
17
|
+
gatewayPath: string;
|
|
18
|
+
/** Primary tag id used in the loader URL (first `G-*`, else first id). */
|
|
19
|
+
primaryTagId: string;
|
|
20
|
+
/** All tag ids to `gtag('config', id, ...)` after load. */
|
|
21
|
+
tagIds: string[];
|
|
22
|
+
/** Extra per-tag config. Currently only used to set `server_container_url`. */
|
|
23
|
+
configParams?: Record<string, unknown>;
|
|
24
|
+
/** Whether gtag.js should send its own `page_view` when configured. */
|
|
25
|
+
sendPageView?: boolean;
|
|
26
|
+
/** Enable `debug_mode` on gtag configs. */
|
|
27
|
+
debugMode?: boolean;
|
|
28
|
+
/** Enable `conversion_linker` (default true in gtag.js). */
|
|
29
|
+
conversionLinker?: boolean;
|
|
30
|
+
/** Domains for the `linker` feature (cross-domain). */
|
|
31
|
+
linkerDomains?: string[];
|
|
32
|
+
}
|
|
33
|
+
export interface LoadedGtag {
|
|
34
|
+
gtag: GtagFn;
|
|
35
|
+
dataLayer: unknown[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Ensure a single `window.dataLayer` + `window.gtag` pair exists. Safe to
|
|
39
|
+
* call multiple times — returns the same references. Matches Google's
|
|
40
|
+
* reference snippet:
|
|
41
|
+
*
|
|
42
|
+
* window.dataLayer = window.dataLayer || [];
|
|
43
|
+
* function gtag(){ dataLayer.push(arguments); }
|
|
44
|
+
*/
|
|
45
|
+
export declare function ensureDataLayer(): LoadedGtag;
|
|
46
|
+
/**
|
|
47
|
+
* Inject the gtag.js <script> tag pointed at the vTilt gateway. The loader
|
|
48
|
+
* script is fire-and-forget; the dataLayer/gtag shim queues any calls we
|
|
49
|
+
* make before the script finishes.
|
|
50
|
+
*/
|
|
51
|
+
export declare function loadGtagScript(options: GtagLoaderOptions, onLoaded?: () => void, onError?: (err: Error) => void): HTMLScriptElement | null;
|
|
52
|
+
/**
|
|
53
|
+
* Initialize gtag (`'js'` + per-id `'config'`) and — if provided — seed
|
|
54
|
+
* conversion linker and cross-domain `linker` params. This runs through
|
|
55
|
+
* the dataLayer shim, so calls made before `gtag.js` finishes loading are
|
|
56
|
+
* replayed automatically once the script boots.
|
|
57
|
+
*/
|
|
58
|
+
export declare function installGtagConfigs(gtag: GtagFn, options: GtagLoaderOptions): void;
|
|
59
|
+
/**
|
|
60
|
+
* Derive loader options from the server-provided feature config.
|
|
61
|
+
*
|
|
62
|
+
* There are exactly two proxy resolutions:
|
|
63
|
+
*
|
|
64
|
+
* | proxyMode | base | path |
|
|
65
|
+
* | ---------- | ------------------------------------------------- | ----- |
|
|
66
|
+
* | `proxied` | `api_host` verbatim (or `location.origin`) | `/gt` |
|
|
67
|
+
* | `direct` | `https://www.googletagmanager.com` | `""` |
|
|
68
|
+
*
|
|
69
|
+
* In proxied mode `api_host` is treated as a verbatim URL prefix — matching
|
|
70
|
+
* how the rest of the SDK builds URLs (`capture`, array loader, chat). It
|
|
71
|
+
* may be any of:
|
|
72
|
+
*
|
|
73
|
+
* - a full URL with a path, e.g. `https://proxy.example.com/v1`
|
|
74
|
+
* - a bare origin, e.g. `https://proxy.example.com`
|
|
75
|
+
* - a relative path, e.g. `/vt` (first-party reverse proxy)
|
|
76
|
+
*
|
|
77
|
+
* For `transport_url` we still need an absolute URL, so a relative
|
|
78
|
+
* `api_host` is expanded against `window.location.origin` in that one
|
|
79
|
+
* place. The loader `<script>` `src` itself keeps the original relative
|
|
80
|
+
* form and is resolved by the browser against the document.
|
|
81
|
+
*
|
|
82
|
+
* Customers who self-host a proxy simply point `api_host` at it — the
|
|
83
|
+
* proxy must implement `/gt/*` under that prefix.
|
|
84
|
+
*/
|
|
85
|
+
export declare function buildLoaderOptions(config: GoogleTagClientConfig, apiHost: string | undefined): GtagLoaderOptions | null;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { GoogleTagGateway, type GoogleTagGatewayFeatureConfig, type DeliveryLogEntry, } from "./google-tag-gateway";
|
|
2
|
+
export { createPublicGtagApi, type PublicGtagApi } from "./public-api";
|
|
3
|
+
export { forwardEvent, buildConversionParams } from "./event-bridge";
|
|
4
|
+
export { ensureDataLayer, loadGtagScript, installGtagConfigs, buildLoaderOptions, type GtagLoaderOptions, } from "./gtag-loader";
|
|
5
|
+
export { pushConsentDefault, pushConsentUpdate, buildConsentPayload, type GtagFn, type GoogleConsentPayload, } from "./consent-bridge";
|
|
6
|
+
export { buildHashedUserData, type HashedUserData, type HashedUserAddress, type RawUserData, } from "./enhanced-conversions";
|
|
7
|
+
export { normalizeEmail, normalizePhone, normalizeBasic, normalizePostalCode, getByPath, coerceNumber, coerceString, } from "./normalize";
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Google-specific normalization for Enhanced Conversions.
|
|
3
|
+
*
|
|
4
|
+
* Follows the rules documented in
|
|
5
|
+
* https://support.google.com/google-ads/answer/13262500 — phone numbers must be
|
|
6
|
+
* in E.164 format, emails lowercased with provider-specific dot stripping on
|
|
7
|
+
* gmail/googlemail, and names/addresses normalized to lowercase without
|
|
8
|
+
* leading/trailing whitespace.
|
|
9
|
+
*/
|
|
10
|
+
export declare function normalizeEmail(raw: string | null | undefined): string | null;
|
|
11
|
+
/**
|
|
12
|
+
* Normalize a phone number to E.164. If the input has no leading '+' we
|
|
13
|
+
* cannot reliably infer the country code, so we return null rather than
|
|
14
|
+
* send an unhashable/ambiguous value — Enhanced Conversions treats missing
|
|
15
|
+
* fields as acceptable.
|
|
16
|
+
*/
|
|
17
|
+
export declare function normalizePhone(raw: string | null | undefined): string | null;
|
|
18
|
+
export declare function normalizeBasic(raw: string | null | undefined): string | null;
|
|
19
|
+
/** Postal codes: collapse whitespace, uppercase (some ISO formats are alpha). */
|
|
20
|
+
export declare function normalizePostalCode(raw: string | null | undefined): string | null;
|
|
21
|
+
/**
|
|
22
|
+
* Read a nested path from an object using dot notation. Used by the
|
|
23
|
+
* event-bridge to extract `value`/`currency`/`transaction_id` from the
|
|
24
|
+
* captured event payload based on the admin-configured path.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getByPath(source: unknown, path: string | null | undefined): unknown;
|
|
27
|
+
export declare function coerceNumber(value: unknown): number | undefined;
|
|
28
|
+
export declare function coerceString(value: unknown): string | undefined;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `vt.gtag(...)` escape hatch.
|
|
3
|
+
*
|
|
4
|
+
* Power users may need to call `gtag` directly (e.g. to fire a custom
|
|
5
|
+
* conversion from a third-party widget or to set a debug param). We expose
|
|
6
|
+
* a flat passthrough that:
|
|
7
|
+
*
|
|
8
|
+
* - queues calls made before the SDK has booted (keeps them in FIFO order),
|
|
9
|
+
* - queues calls made before `gtag.js` has loaded (forwarded via the
|
|
10
|
+
* `dataLayer` shim, which naturally buffers until the real script runs),
|
|
11
|
+
* - records every call so tests + the delivery log can see them.
|
|
12
|
+
*/
|
|
13
|
+
import type { GtagFn } from "./consent-bridge";
|
|
14
|
+
export type GtagCall = unknown[];
|
|
15
|
+
export interface PublicGtagApi {
|
|
16
|
+
/** The flat function exposed as `vt.gtag(...)` */
|
|
17
|
+
call: GtagFn;
|
|
18
|
+
/** Flush queued calls to a now-ready gtag implementation. */
|
|
19
|
+
flush(target: GtagFn): void;
|
|
20
|
+
/** Observability: recent calls sent through this API (ring buffer). */
|
|
21
|
+
getRecentCalls(): GtagCall[];
|
|
22
|
+
}
|
|
23
|
+
export declare function createPublicGtagApi(): PublicGtagApi;
|
|
@@ -10,8 +10,6 @@ import type { VTilt } from "../vtilt";
|
|
|
10
10
|
import type { VTiltConfig } from "../types";
|
|
11
11
|
import type { Feature, FeatureConfig } from "../feature";
|
|
12
12
|
export interface HistoryAutocaptureConfig extends FeatureConfig {
|
|
13
|
-
/** Whether to capture pageviews on history changes */
|
|
14
|
-
enabled?: boolean;
|
|
15
13
|
}
|
|
16
14
|
/**
|
|
17
15
|
* History Autocapture Feature
|
|
@@ -39,6 +37,8 @@ export declare class HistoryAutocapture implements Feature {
|
|
|
39
37
|
onConfigUpdate(config: VTiltConfig): void;
|
|
40
38
|
private _start;
|
|
41
39
|
private _patchHistoryMethods;
|
|
40
|
+
/** $pageleave must run while the current URL still reflects the page being left. */
|
|
41
|
+
private _emitPageleaveIfPathWillChange;
|
|
42
42
|
private _setupPopstateListener;
|
|
43
43
|
private _capturePageview;
|
|
44
44
|
}
|
|
@@ -10,4 +10,4 @@
|
|
|
10
10
|
export { SessionRecordingWrapper, LAZY_LOADING, type SessionRecordingStatus, type LazyLoadedSessionRecordingInterface, } from "./session-recording-wrapper";
|
|
11
11
|
export { LazyLoadedSessionRecording } from "./session-recording";
|
|
12
12
|
export type { SessionRecordingConfig, SessionStartReason, RecordOptions, RRWebRecord, SnapshotBuffer, CanvasRecordingConfig, NetworkPayloadCaptureConfig, MaskingConfig, TriggerType, } from "./types";
|
|
13
|
-
export {
|
|
13
|
+
export { estimateSize, truncateLargeConsoleLogs, splitBuffer, isSessionIdleEvent, isRecordingPausedEvent, isInteractiveEvent, RECORDING_MAX_EVENT_SIZE, RECORDING_BUFFER_TIMEOUT, RECORDING_IDLE_THRESHOLD_MS, } from "./session-recording-utils";
|