@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
package/dist/user-manager.d.ts
CHANGED
|
@@ -10,12 +10,26 @@
|
|
|
10
10
|
* - user_properties: Custom properties set via identify/setUserProperties
|
|
11
11
|
* - user_state: "anonymous" or "identified"
|
|
12
12
|
*/
|
|
13
|
-
import { UserIdentity,
|
|
13
|
+
import { UserIdentity, IdentityUpdate, PersistenceMethod } from "./types";
|
|
14
14
|
export declare class UserManager {
|
|
15
15
|
private storage;
|
|
16
16
|
private userIdentity;
|
|
17
|
-
private
|
|
17
|
+
private _isFirstVisit;
|
|
18
18
|
constructor(storageMethod?: PersistenceMethod, cross_subdomain?: boolean);
|
|
19
|
+
/**
|
|
20
|
+
* Hydrate identity from persistent storage.
|
|
21
|
+
*
|
|
22
|
+
* Called by VTilt._boot() once the browser environment is guaranteed ready
|
|
23
|
+
* (DOMContentLoaded). This is the only code path that reads from
|
|
24
|
+
* localStorage / cookies. If storage is empty (first visit), the generated
|
|
25
|
+
* IDs are persisted so they survive the next page load.
|
|
26
|
+
*/
|
|
27
|
+
hydrateFromStorage(): void;
|
|
28
|
+
/**
|
|
29
|
+
* Generate an ephemeral in-memory identity with no storage access.
|
|
30
|
+
* Used by the constructor so the SDK is safe to instantiate in SSR.
|
|
31
|
+
*/
|
|
32
|
+
private generateEphemeralIdentity;
|
|
19
33
|
/**
|
|
20
34
|
* Get current user identity
|
|
21
35
|
*/
|
|
@@ -45,42 +59,20 @@ export declare class UserManager {
|
|
|
45
59
|
*/
|
|
46
60
|
getUserState(): "anonymous" | "identified";
|
|
47
61
|
/**
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Set user properties without changing distinct ID
|
|
62
|
+
* Returns true if this is the user's first visit (no prior anonymous_id
|
|
63
|
+
* in storage when the SDK booted). Resets after first call.
|
|
64
|
+
* Matches GA4's _fv=1 semantics.
|
|
53
65
|
*/
|
|
54
|
-
|
|
66
|
+
consumeFirstVisit(): boolean;
|
|
55
67
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
68
|
+
* Apply batched identity changes with a single save.
|
|
69
|
+
* Replaces individual setters to avoid multiple storage writes per operation.
|
|
58
70
|
*/
|
|
59
|
-
|
|
71
|
+
applyUpdate(update: IdentityUpdate): void;
|
|
60
72
|
/**
|
|
61
|
-
*
|
|
73
|
+
* Reset identity to anonymous state. Generates new IDs and clears user data.
|
|
62
74
|
*/
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Update user state (internal use - for VTilt)
|
|
66
|
-
*/
|
|
67
|
-
setUserState(state: "anonymous" | "identified"): void;
|
|
68
|
-
/**
|
|
69
|
-
* Update user properties (internal use - for VTilt)
|
|
70
|
-
*/
|
|
71
|
-
updateUserProperties(userPropertiesToSet?: Record<string, any>, userPropertiesToSetOnce?: Record<string, any>): void;
|
|
72
|
-
/**
|
|
73
|
-
* Set device ID if not already set (internal use - for VTilt)
|
|
74
|
-
*/
|
|
75
|
-
ensureDeviceId(deviceId: string): void;
|
|
76
|
-
/**
|
|
77
|
-
* Create an alias to link two distinct IDs
|
|
78
|
-
*/
|
|
79
|
-
createAlias(alias: string, original?: string): AliasEvent | null;
|
|
80
|
-
/**
|
|
81
|
-
* Check if distinct ID is string-like (hardcoded string) - public for validation
|
|
82
|
-
*/
|
|
83
|
-
isDistinctIdStringLikePublic(distinctId: string): boolean;
|
|
75
|
+
reset(resetDeviceId?: boolean): void;
|
|
84
76
|
/**
|
|
85
77
|
* Set initial person info
|
|
86
78
|
*/
|
|
@@ -127,28 +119,10 @@ export declare class UserManager {
|
|
|
127
119
|
* Register a value once (only if not already set)
|
|
128
120
|
*/
|
|
129
121
|
private register_once;
|
|
130
|
-
/**
|
|
131
|
-
* Generate a new anonymous ID
|
|
132
|
-
*/
|
|
133
122
|
private generateAnonymousId;
|
|
134
|
-
/**
|
|
135
|
-
* Generate a new device ID
|
|
136
|
-
*/
|
|
137
123
|
private generateDeviceId;
|
|
138
124
|
/**
|
|
139
|
-
*
|
|
140
|
-
*/
|
|
141
|
-
private getPersonPropertiesHash;
|
|
142
|
-
/**
|
|
143
|
-
* Validate distinct ID
|
|
144
|
-
*/
|
|
145
|
-
private isValidDistinctId;
|
|
146
|
-
/**
|
|
147
|
-
* Check if distinct ID is string-like (hardcoded string)
|
|
148
|
-
*/
|
|
149
|
-
private isDistinctIdStringLike;
|
|
150
|
-
/**
|
|
151
|
-
* Update storage method at runtime
|
|
125
|
+
* Update storage method at runtime.
|
|
152
126
|
*/
|
|
153
127
|
updateStorageMethod(method: PersistenceMethod, cross_subdomain?: boolean): void;
|
|
154
128
|
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base64 encoding for binary data.
|
|
3
|
+
*
|
|
4
|
+
* Used by the request layer (`request.ts`) and session recording
|
|
5
|
+
* (`extensions/replay/session-recording.ts`) to send gzip-compressed bodies
|
|
6
|
+
* over `navigator.sendBeacon` as **text** rather than as a binary `Blob`.
|
|
7
|
+
*
|
|
8
|
+
* Why this exists:
|
|
9
|
+
*
|
|
10
|
+
* `sendBeacon(url, blob)` with a binary `Blob` (e.g. a gzipped Uint8Array)
|
|
11
|
+
* is racy. Browsers queue the beacon synchronously, then serialize the
|
|
12
|
+
* body to the network *after* the JS context has been torn down on
|
|
13
|
+
* `pagehide` / tab discard. If the underlying `ArrayBuffer` is detached
|
|
14
|
+
* or GC'd before that flush, the wire body ends up empty or truncated —
|
|
15
|
+
* while the URL we already committed still carries `?compression=gzip-js`.
|
|
16
|
+
* The server then tries `gunzipSync` against an empty buffer and throws
|
|
17
|
+
* `Z_BUF_ERROR: unexpected end of file`.
|
|
18
|
+
*
|
|
19
|
+
* Sending the gzip bytes as a base64 *string* sidesteps the problem
|
|
20
|
+
* entirely: `Blob([string])` owns its own UTF-8 encoded copy, and
|
|
21
|
+
* `sendBeacon` is reliable for text bodies. The server already accepts
|
|
22
|
+
* `?compression=base64` for this exact path.
|
|
23
|
+
*/
|
|
24
|
+
/**
|
|
25
|
+
* Encode a `Uint8Array` to a base64 string using `btoa`.
|
|
26
|
+
*
|
|
27
|
+
* Chunks the bytes through `String.fromCharCode` so we don't blow the
|
|
28
|
+
* argument-count limit on `apply()` for large buffers.
|
|
29
|
+
*/
|
|
30
|
+
export declare function uint8ArrayToBase64(bytes: Uint8Array): string;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bot / crawler detection for the browser SDK.
|
|
3
|
+
*
|
|
4
|
+
* User-agent patterns are aligned with PostHog's `@posthog/core` bot list
|
|
5
|
+
* (https://github.com/PostHog/posthog-js — `utils/bot-detection.ts`) so
|
|
6
|
+
* behaviour matches what PostHog integrators expect.
|
|
7
|
+
*
|
|
8
|
+
* Keep in sync with `app/src/lib/tracking/bot-detection.ts`.
|
|
9
|
+
*/
|
|
10
|
+
/** Substrings matched case-insensitively against `User-Agent` (and SDK `navigator.userAgent`). */
|
|
11
|
+
export declare const DEFAULT_BLOCKED_UA_STRS: readonly string[];
|
|
12
|
+
/**
|
|
13
|
+
* Returns true when the user agent matches a known bot/crawler pattern.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isBlockedUA(ua: string | undefined | null, customBlockedUserAgents?: string[]): boolean;
|
|
16
|
+
/** Subset of `Navigator.userAgentData` used for bot checks. */
|
|
17
|
+
export interface NavigatorUAData {
|
|
18
|
+
brands?: {
|
|
19
|
+
brand: string;
|
|
20
|
+
version: string;
|
|
21
|
+
}[];
|
|
22
|
+
platform?: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Browser-side bot check (PostHog `isLikelyBot` parity): UA string, CH brand
|
|
26
|
+
* hints, and `navigator.webdriver`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function isLikelyBot(nav: Navigator | undefined, customBlockedUserAgents?: string[]): boolean;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { VTiltConfig } from "../types";
|
|
2
|
+
/** Header that carries the project token on every authenticated SDK call.
|
|
3
|
+
* Matches the server-side `withPublicProjectAccess` wrapper, which reads
|
|
4
|
+
* the same name (`request.headers.get('x-api-key')`) before falling back
|
|
5
|
+
* to the `?token=` query parameter. */
|
|
6
|
+
export declare const PROJECT_TOKEN_HEADER = "x-api-key";
|
|
7
|
+
export interface BuildEndpointUrlOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Append `?token=<token>` to the URL. Off by default so the URL stays
|
|
10
|
+
* clean — privacy/ad blockers commonly match on `?token=` in known
|
|
11
|
+
* analytics origins, and the cleaner the URL is the less likely a
|
|
12
|
+
* legitimate request gets cancelled with `ERR_BLOCKED_BY_CLIENT`.
|
|
13
|
+
*
|
|
14
|
+
* Pass `true` only for transports that cannot carry a custom header
|
|
15
|
+
* (i.e. `navigator.sendBeacon`), where the query parameter is still
|
|
16
|
+
* the only practical way to authenticate the request.
|
|
17
|
+
*/
|
|
18
|
+
includeTokenQuery?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Resolves a path + project token to a full request URL.
|
|
22
|
+
* Used by the main client and by lazy chunks (e.g. session recording) that
|
|
23
|
+
* only have access to `getConfig()` and must not depend on `VTilt` prototype methods.
|
|
24
|
+
*
|
|
25
|
+
* By default the returned URL does NOT carry the project token. Callers
|
|
26
|
+
* are expected to attach it via the `x-api-key` header (see
|
|
27
|
+
* `getProjectTokenHeader`). For `sendBeacon` paths where headers aren't
|
|
28
|
+
* supported, pass `{ includeTokenQuery: true }`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildEndpointUrlFromConfig(config: Pick<VTiltConfig, "api_host" | "token">, path: string, options?: BuildEndpointUrlOptions): string;
|
|
31
|
+
/**
|
|
32
|
+
* Returns the auth header dict to merge into a `fetch` / `XMLHttpRequest`
|
|
33
|
+
* call. Returns an empty object when no token is configured so callers
|
|
34
|
+
* can spread it unconditionally.
|
|
35
|
+
*/
|
|
36
|
+
export declare function getProjectTokenHeader(config: Pick<VTiltConfig, "token">): Record<string, string>;
|
|
@@ -96,6 +96,7 @@ export declare const VTiltEvents: {
|
|
|
96
96
|
readonly SESSION_ROTATED: "session:rotated";
|
|
97
97
|
readonly FEATURE_STARTED: "feature:started";
|
|
98
98
|
readonly FEATURE_STOPPED: "feature:stopped";
|
|
99
|
+
readonly CONSENT_UPDATED: "consent:updated";
|
|
99
100
|
readonly EVENT_CAPTURED: "event:captured";
|
|
100
101
|
readonly EVENT_SENT: "event:sent";
|
|
101
102
|
readonly EVENT_FAILED: "event:failed";
|
package/dist/utils/globals.d.ts
CHANGED
|
@@ -13,7 +13,6 @@ export interface LazyLoadedSessionRecordingInterface {
|
|
|
13
13
|
sessionId: string;
|
|
14
14
|
status: string;
|
|
15
15
|
isStarted: boolean;
|
|
16
|
-
onRemoteConfig?: (response: any) => void;
|
|
17
16
|
log: (message: string, level: "log" | "warn" | "error") => void;
|
|
18
17
|
updateConfig: (config: any) => void;
|
|
19
18
|
}
|
|
@@ -136,6 +135,8 @@ export interface ChatConfig {
|
|
|
136
135
|
offlineMessage?: string;
|
|
137
136
|
/** Collect email when offline */
|
|
138
137
|
collectEmailOffline?: boolean;
|
|
138
|
+
/** Bubble appearance and behavior */
|
|
139
|
+
bubble?: BubbleConfig;
|
|
139
140
|
/** Called when widget is opened */
|
|
140
141
|
onWidgetOpen?: () => void;
|
|
141
142
|
/** Called when widget is closed */
|
|
@@ -171,6 +172,47 @@ export interface ChatTheme {
|
|
|
171
172
|
userBubbleColor?: string;
|
|
172
173
|
agentBubbleColor?: string;
|
|
173
174
|
}
|
|
175
|
+
/**
|
|
176
|
+
* First argument to `vt.sendChatMessage()` / {@link LazyLoadedChatInterface.sendMessage}.
|
|
177
|
+
*
|
|
178
|
+
* Plain strings stay plain text for backward compatibility. Use `{ markdown: '...' }`,
|
|
179
|
+
* `{ html: '...' }`, or `options.format` for rich content.
|
|
180
|
+
*/
|
|
181
|
+
export type SendChatMessageContent = string | {
|
|
182
|
+
text: string;
|
|
183
|
+
} | {
|
|
184
|
+
markdown: string;
|
|
185
|
+
} | {
|
|
186
|
+
html: string;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Options for `vt.sendChatMessage()` / {@link LazyLoadedChatInterface.sendMessage}.
|
|
190
|
+
*/
|
|
191
|
+
export interface SendChatMessageOptions {
|
|
192
|
+
/**
|
|
193
|
+
* Target conversation: `'new'` creates a channel, a UUID selects an existing one,
|
|
194
|
+
* omit to use the active conversation (must already be in conversation view).
|
|
195
|
+
*/
|
|
196
|
+
channel?: "new" | string;
|
|
197
|
+
/**
|
|
198
|
+
* Open the widget panel before sending. Default: true. Pass `open: false` to send without opening.
|
|
199
|
+
*/
|
|
200
|
+
open?: boolean;
|
|
201
|
+
/**
|
|
202
|
+
* Applies when the first argument is a plain string. Default: `text`.
|
|
203
|
+
* `markdown` and `html` use the same sanitized rendering as AI/agent messages.
|
|
204
|
+
*/
|
|
205
|
+
format?: "text" | "markdown" | "html";
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Chat bubble appearance and behavior configuration
|
|
209
|
+
*/
|
|
210
|
+
export interface BubbleConfig {
|
|
211
|
+
/** Allow user to drag the bubble to reposition (default: false) */
|
|
212
|
+
draggable?: boolean;
|
|
213
|
+
/** Show the bubble on load (default: true). Set to false to control via vt.chat.show() */
|
|
214
|
+
visible?: boolean;
|
|
215
|
+
}
|
|
174
216
|
/**
|
|
175
217
|
* Interface for lazy-loaded chat widget (set by chat.ts entrypoint)
|
|
176
218
|
*/
|
|
@@ -195,12 +237,39 @@ export interface LazyLoadedChatInterface {
|
|
|
195
237
|
createChannel(): Promise<void>;
|
|
196
238
|
/** Go back to channel list from conversation view */
|
|
197
239
|
goToChannelList(): void;
|
|
198
|
-
sendMessage(content:
|
|
240
|
+
sendMessage(content: SendChatMessageContent, options?: SendChatMessageOptions): Promise<void>;
|
|
199
241
|
markAsRead(): void;
|
|
200
242
|
onMessage(callback: (message: ChatMessage) => void): () => void;
|
|
201
243
|
onTyping(callback: (isTyping: boolean, senderType: string) => void): () => void;
|
|
202
244
|
onConnectionChange(callback: (connected: boolean) => void): () => void;
|
|
203
245
|
updateConfig?(config: ChatConfig): void;
|
|
246
|
+
registerWidget?(definition: {
|
|
247
|
+
type: string;
|
|
248
|
+
toolDescription: string;
|
|
249
|
+
parameters: Record<string, {
|
|
250
|
+
type: string;
|
|
251
|
+
description: string;
|
|
252
|
+
}>;
|
|
253
|
+
render: (params: Record<string, unknown>, context: {
|
|
254
|
+
channelId: string;
|
|
255
|
+
distinctId: string;
|
|
256
|
+
primaryColor: string;
|
|
257
|
+
apiBase: string;
|
|
258
|
+
token: string;
|
|
259
|
+
messageId: string;
|
|
260
|
+
}) => string;
|
|
261
|
+
onAction: (action: string, data: Record<string, unknown>, context: {
|
|
262
|
+
channelId: string;
|
|
263
|
+
distinctId: string;
|
|
264
|
+
primaryColor: string;
|
|
265
|
+
apiBase: string;
|
|
266
|
+
token: string;
|
|
267
|
+
messageId: string;
|
|
268
|
+
}) => Promise<{
|
|
269
|
+
replaceHTML?: string;
|
|
270
|
+
sendMessage?: string;
|
|
271
|
+
}>;
|
|
272
|
+
}): void;
|
|
204
273
|
destroy(): void;
|
|
205
274
|
}
|
|
206
275
|
/**
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
export * from "./type-guards";
|
|
2
2
|
export * from "./safewrap";
|
|
3
|
+
export * from "./endpoint-url";
|
|
3
4
|
export * from "./event-emitter";
|
|
5
|
+
export * from "./transport-health";
|
|
6
|
+
export { getLogger, resolveLogLevel, setLevelFromConfig, type LogLevel, type Logger, } from "./logger";
|
|
4
7
|
/**
|
|
5
8
|
* Generate a short, URL-safe random ID (nanoid-compatible).
|
|
6
9
|
* Uses crypto.getRandomValues for entropy — same approach as nanoid.
|
|
@@ -12,7 +15,23 @@ export declare function generateId(size?: number): string;
|
|
|
12
15
|
*/
|
|
13
16
|
export declare function isValidUserAgent(userAgent: string): boolean;
|
|
14
17
|
/**
|
|
15
|
-
*
|
|
18
|
+
* Maximum serialized event payload size, in bytes (1 MB).
|
|
19
|
+
*
|
|
20
|
+
* The transport layer gzips bodies before sending and the ingestion API
|
|
21
|
+
* applies its own server-side limits, so this cap exists only to defeat
|
|
22
|
+
* runaway client-side bugs (infinite property growth, accidental DOM dumps).
|
|
23
|
+
*
|
|
24
|
+
* Previously 10 KB, which silently dropped legitimate `$autocapture` events
|
|
25
|
+
* on real apps where 15–25 ancestor elements with framework class soup put
|
|
26
|
+
* the JSON above 10 KB before any user-defined properties were added.
|
|
27
|
+
*
|
|
28
|
+
* Aligned with PostHog's analogous client cap (1 MB).
|
|
29
|
+
*/
|
|
30
|
+
export declare const MAX_PAYLOAD_BYTES = 1048576;
|
|
31
|
+
/**
|
|
32
|
+
* Validate payload string. Used to defend against pathological / runaway
|
|
33
|
+
* payloads only; routine "this event got too big" cases (deep DOM chains)
|
|
34
|
+
* must NOT be silently dropped here. See {@link MAX_PAYLOAD_BYTES}.
|
|
16
35
|
*/
|
|
17
36
|
export declare function isValidPayload(payloadStr: string): boolean;
|
|
18
37
|
/**
|
|
@@ -45,7 +64,3 @@ export declare function stripEmptyProperties<T extends Record<string, any>>(obj:
|
|
|
45
64
|
* Strip leading dollar sign from string
|
|
46
65
|
*/
|
|
47
66
|
export declare function stripLeadingDollar(str: string): string;
|
|
48
|
-
/**
|
|
49
|
-
* Check if value is null
|
|
50
|
-
*/
|
|
51
|
-
export declare function isNull(value: any): boolean;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configurable logger for SDK development tracing.
|
|
3
|
+
*
|
|
4
|
+
* Level taxonomy (single source of truth — every SDK file follows it):
|
|
5
|
+
* - error: SDK or integrator-supplied input is broken (failed network with no
|
|
6
|
+
* retry left, invalid identify input, storage write rejected, feature
|
|
7
|
+
* crashed inside safewrap).
|
|
8
|
+
* - warn: degraded but recovering (deprecated API, falling back from
|
|
9
|
+
* localStorage to memory, retrying a failed request, ignoring an
|
|
10
|
+
* unsupported config option, browser feature missing).
|
|
11
|
+
* - info: one-time lifecycle events the integrator wants in the console
|
|
12
|
+
* while wiring the SDK up (SDK initialised, autocapture started/stopped,
|
|
13
|
+
* replay started/stopped, chat connected, person identified).
|
|
14
|
+
* - debug: per-event trace useful when filing a bug (every captured event
|
|
15
|
+
* name, every autocapture skip with reason, every queue flush, request
|
|
16
|
+
* URLs, decide-endpoint payload).
|
|
17
|
+
*
|
|
18
|
+
* Default floor is 'warn' — so SDK errors and warnings always reach the
|
|
19
|
+
* integrator without opt-in (matches Amplitude/PostHog/Sentry). Use 'none'
|
|
20
|
+
* to silence everything (e.g. shared kiosk consoles).
|
|
21
|
+
*
|
|
22
|
+
* Resolution precedence for the active level:
|
|
23
|
+
* 1. log_level / debug passed to vt.init() — locks the level.
|
|
24
|
+
* 2. Remote config from /decide (diagnostics.defaultLogLevel) — applied only
|
|
25
|
+
* if init did not lock the level.
|
|
26
|
+
* 3. Module default 'warn'.
|
|
27
|
+
*/
|
|
28
|
+
import type { VTiltConfig } from "../types";
|
|
29
|
+
export type LogLevel = "none" | "error" | "warn" | "info" | "debug";
|
|
30
|
+
export interface Logger {
|
|
31
|
+
/**
|
|
32
|
+
* Set the active level. When `lockedByInit` is true the level is pinned
|
|
33
|
+
* and subsequent calls to `setLevelFromRemote` are ignored. Use this
|
|
34
|
+
* variant only from the init-config resolver in vtilt.ts.
|
|
35
|
+
*/
|
|
36
|
+
setLevel(level: LogLevel, lockedByInit?: boolean): void;
|
|
37
|
+
/**
|
|
38
|
+
* Apply a level coming from remote config. No-op when the integrator
|
|
39
|
+
* pinned a level at init time, so a hard-coded snippet always wins.
|
|
40
|
+
*/
|
|
41
|
+
setLevelFromRemote(level: LogLevel | null | undefined): void;
|
|
42
|
+
getLevel(): LogLevel;
|
|
43
|
+
isLockedByInit(): boolean;
|
|
44
|
+
debug(...args: unknown[]): void;
|
|
45
|
+
info(...args: unknown[]): void;
|
|
46
|
+
warn(...args: unknown[]): void;
|
|
47
|
+
error(...args: unknown[]): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the global SDK logger. Level is set by VTilt from config (log_level or
|
|
51
|
+
* debug: true) and may later be overridden by remote config via
|
|
52
|
+
* setLevelFromRemote — unless init pinned the level.
|
|
53
|
+
*/
|
|
54
|
+
export declare function getLogger(): Logger;
|
|
55
|
+
/**
|
|
56
|
+
* Resolve a log level from VTiltConfig. Centralises the
|
|
57
|
+
* `log_level ?? (debug ? 'debug' : 'warn')` rule so call sites cannot drift.
|
|
58
|
+
*/
|
|
59
|
+
export declare function resolveLogLevel(config: Pick<VTiltConfig, "log_level" | "debug">): LogLevel;
|
|
60
|
+
/**
|
|
61
|
+
* Apply a config-derived level to the global logger. Pass `lockedByInit`
|
|
62
|
+
* (default true) when the call originates from vt.init() so remote config
|
|
63
|
+
* cannot later override the integrator's choice.
|
|
64
|
+
*/
|
|
65
|
+
export declare function setLevelFromConfig(config: Pick<VTiltConfig, "log_level" | "debug">, lockedByInit?: boolean): LogLevel;
|
|
66
|
+
export declare function __resetLoggerForTests(): void;
|
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
* IE11 doesn't support `new URL`, so we use anchor element
|
|
8
8
|
*/
|
|
9
9
|
export declare function convertToURL(url: string): HTMLAnchorElement | null;
|
|
10
|
+
/**
|
|
11
|
+
* Parse all query parameters from a URL into a key/value map.
|
|
12
|
+
* When a key appears more than once, the last value wins.
|
|
13
|
+
*/
|
|
14
|
+
export declare function getAllQueryParams(url: string): Record<string, string>;
|
|
10
15
|
/**
|
|
11
16
|
* Get query parameter from URL
|
|
12
17
|
*/
|
package/dist/utils/safewrap.d.ts
CHANGED
|
@@ -3,9 +3,14 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Error isolation utilities to prevent feature errors from crashing the SDK.
|
|
5
5
|
* Following PostHog's safewrap pattern.
|
|
6
|
+
*
|
|
7
|
+
* All errors are routed through the central level-gated logger
|
|
8
|
+
* (`getLogger()`) so they respect the integrator's `log_level` choice.
|
|
9
|
+
* `error`-level messages always surface in the default `warn` floor.
|
|
6
10
|
*/
|
|
7
11
|
/**
|
|
8
|
-
* Logger interface for error reporting
|
|
12
|
+
* Logger interface for error reporting. Kept narrow so callers can inject a
|
|
13
|
+
* custom logger in tests without depending on the full `Logger` shape.
|
|
9
14
|
*/
|
|
10
15
|
interface Logger {
|
|
11
16
|
error: (...args: unknown[]) => void;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport health tracking
|
|
3
|
+
*
|
|
4
|
+
* Chromium and Firefox surface `ERR_BLOCKED_BY_CLIENT` (or the equivalent
|
|
5
|
+
* "NetworkError when attempting to fetch") to the JS layer as a generic
|
|
6
|
+
* `TypeError: Failed to fetch` — there is no programmatic way to tell a
|
|
7
|
+
* cancelled-by-extension request apart from an offline or DNS failure.
|
|
8
|
+
*
|
|
9
|
+
* What we can do is notice the pattern. Once a handful of consecutive
|
|
10
|
+
* top-level transport failures happen in a row we treat the transport as
|
|
11
|
+
* "likely blocked" for the rest of the page session and:
|
|
12
|
+
*
|
|
13
|
+
* 1. Stop scheduling new requests (no more retries, no new session
|
|
14
|
+
* recording POSTs). The browser will keep logging
|
|
15
|
+
* `ERR_BLOCKED_BY_CLIENT` for any request the SDK *does* fire, so
|
|
16
|
+
* we minimise that noise by firing fewer of them.
|
|
17
|
+
* 2. Log a single explanatory warning that points integrators at the
|
|
18
|
+
* reverse-proxy escape hatch so they aren't left guessing.
|
|
19
|
+
*
|
|
20
|
+
* A subsequent successful response resets the counter — transient
|
|
21
|
+
* connectivity glitches don't latch the SDK into "blocked" mode.
|
|
22
|
+
*
|
|
23
|
+
* The state is *page-session scoped*. We deliberately do not persist it
|
|
24
|
+
* to storage: ad-blocker filter lists change, the user might disable a
|
|
25
|
+
* blocker for our domain, and we don't want a single bad page load to
|
|
26
|
+
* cripple every future visit.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Record a top-level transport failure (network error / blocked / aborted).
|
|
30
|
+
*
|
|
31
|
+
* `scope` is the SDK module reporting the failure (e.g. `"replay"`,
|
|
32
|
+
* `"capture"`); it's used purely for the warning log message.
|
|
33
|
+
*/
|
|
34
|
+
export declare function markTransportFailure(scope: string): void;
|
|
35
|
+
/**
|
|
36
|
+
* Record a successful response. Resets the failure counter; a future run
|
|
37
|
+
* of failures must accumulate from zero before flipping the flag again.
|
|
38
|
+
*
|
|
39
|
+
* Does NOT clear the `blocked` flag: once the SDK has decided to back
|
|
40
|
+
* off in a session it stays backed off for the lifetime of that page.
|
|
41
|
+
* Without this rule a single intermittent success between failures would
|
|
42
|
+
* silently re-enable the retry storm we are trying to suppress.
|
|
43
|
+
*/
|
|
44
|
+
export declare function markTransportSuccess(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Once the SDK believes the transport is blocked, this returns `true`
|
|
47
|
+
* for the rest of the page session. Callers should skip scheduling work
|
|
48
|
+
* that would otherwise spam `ERR_BLOCKED_BY_CLIENT` into the console.
|
|
49
|
+
*/
|
|
50
|
+
export declare function isTransportLikelyBlocked(): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* Test-only reset. Not exported from the package barrel — only
|
|
53
|
+
* `__tests__` should use it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function __resetTransportHealthForTests(): void;
|
|
@@ -39,7 +39,7 @@ export declare function isObject(x: unknown): x is Record<string, unknown>;
|
|
|
39
39
|
/**
|
|
40
40
|
* Check if value is a function
|
|
41
41
|
*/
|
|
42
|
-
export declare function isFunction(x: unknown): x is
|
|
42
|
+
export declare function isFunction(x: unknown): x is (...args: unknown[]) => unknown;
|
|
43
43
|
/**
|
|
44
44
|
* Check if value is a Date object
|
|
45
45
|
*/
|