@v-tilt/browser 1.0.10 → 1.1.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/LICENSE +21 -0
- 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/constants.d.ts +1 -0
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/module.d.ts +57 -4
- package/dist/module.js +1 -1
- package/dist/module.js.map +1 -1
- package/dist/module.no-external.d.ts +57 -4
- package/dist/module.no-external.js +1 -1
- package/dist/module.no-external.js.map +1 -1
- package/dist/rate-limiter.d.ts +52 -0
- package/dist/request-queue.d.ts +78 -0
- package/dist/request.d.ts +54 -0
- package/dist/retry-queue.d.ts +64 -0
- package/dist/types.d.ts +1 -0
- package/dist/user-manager.d.ts +21 -0
- package/dist/utils/event-utils.d.ts +35 -17
- package/dist/utils/index.d.ts +21 -0
- package/dist/utils/request-utils.d.ts +17 -0
- package/dist/vtilt.d.ts +40 -8
- package/lib/constants.d.ts +1 -0
- package/lib/constants.js +2 -1
- package/lib/rate-limiter.d.ts +52 -0
- package/lib/rate-limiter.js +80 -0
- package/lib/request-queue.d.ts +78 -0
- package/lib/request-queue.js +156 -0
- package/lib/request.d.ts +54 -0
- package/lib/request.js +265 -0
- package/lib/retry-queue.d.ts +64 -0
- package/lib/retry-queue.js +182 -0
- package/lib/types.d.ts +1 -0
- package/lib/user-manager.d.ts +21 -0
- package/lib/user-manager.js +66 -0
- package/lib/utils/event-utils.d.ts +35 -17
- package/lib/utils/event-utils.js +247 -118
- package/lib/utils/index.d.ts +21 -0
- package/lib/utils/index.js +58 -0
- package/lib/utils/request-utils.d.ts +17 -0
- package/lib/utils/request-utils.js +80 -0
- package/lib/vtilt.d.ts +40 -8
- package/lib/vtilt.js +161 -11
- package/package.json +61 -61
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Queue - Event Batching (PostHog-style)
|
|
3
|
+
*
|
|
4
|
+
* Batches multiple events together and sends them at configurable intervals.
|
|
5
|
+
* This reduces the number of HTTP requests significantly for active users.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Configurable flush interval (default 3 seconds)
|
|
9
|
+
* - Batches events by URL/batchKey
|
|
10
|
+
* - Uses sendBeacon on page unload for reliable delivery
|
|
11
|
+
* - Converts absolute timestamps to relative offsets before sending
|
|
12
|
+
*/
|
|
13
|
+
import type { TrackingEvent } from "./types";
|
|
14
|
+
export declare const DEFAULT_FLUSH_INTERVAL_MS = 3000;
|
|
15
|
+
export interface QueuedRequest {
|
|
16
|
+
url: string;
|
|
17
|
+
event: TrackingEvent;
|
|
18
|
+
batchKey?: string;
|
|
19
|
+
transport?: "xhr" | "sendBeacon";
|
|
20
|
+
}
|
|
21
|
+
export interface BatchedRequest {
|
|
22
|
+
url: string;
|
|
23
|
+
events: TrackingEvent[];
|
|
24
|
+
batchKey?: string;
|
|
25
|
+
transport?: "xhr" | "sendBeacon";
|
|
26
|
+
}
|
|
27
|
+
export interface RequestQueueConfig {
|
|
28
|
+
flush_interval_ms?: number;
|
|
29
|
+
}
|
|
30
|
+
export declare class RequestQueue {
|
|
31
|
+
private _isPaused;
|
|
32
|
+
private _queue;
|
|
33
|
+
private _flushTimeout?;
|
|
34
|
+
private _flushTimeoutMs;
|
|
35
|
+
private _sendRequest;
|
|
36
|
+
constructor(sendRequest: (req: BatchedRequest) => void, config?: RequestQueueConfig);
|
|
37
|
+
/**
|
|
38
|
+
* Get the current queue length
|
|
39
|
+
*/
|
|
40
|
+
get length(): number;
|
|
41
|
+
/**
|
|
42
|
+
* Enqueue an event for batched sending
|
|
43
|
+
*/
|
|
44
|
+
enqueue(req: QueuedRequest): void;
|
|
45
|
+
/**
|
|
46
|
+
* Flush all queued events immediately using sendBeacon
|
|
47
|
+
* Called on page unload to ensure events are delivered
|
|
48
|
+
*/
|
|
49
|
+
unload(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Enable the queue and start flushing
|
|
52
|
+
*/
|
|
53
|
+
enable(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Pause the queue (stops flushing but keeps events)
|
|
56
|
+
*/
|
|
57
|
+
pause(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Force an immediate flush
|
|
60
|
+
*/
|
|
61
|
+
flush(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Set up the flush timeout
|
|
64
|
+
*/
|
|
65
|
+
private _setFlushTimeout;
|
|
66
|
+
/**
|
|
67
|
+
* Clear the flush timeout
|
|
68
|
+
*/
|
|
69
|
+
private _clearFlushTimeout;
|
|
70
|
+
/**
|
|
71
|
+
* Flush all queued events now
|
|
72
|
+
*/
|
|
73
|
+
private _flushNow;
|
|
74
|
+
/**
|
|
75
|
+
* Format the queue into batched requests by URL/batchKey
|
|
76
|
+
*/
|
|
77
|
+
private _formatQueue;
|
|
78
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request utilities for vTilt tracking
|
|
3
|
+
*
|
|
4
|
+
* Handles HTTP requests with:
|
|
5
|
+
* - GZip compression (via fflate)
|
|
6
|
+
* - Multiple transport methods (fetch, XHR, sendBeacon)
|
|
7
|
+
* - Automatic fallback between transports
|
|
8
|
+
*
|
|
9
|
+
* Based on PostHog's request.ts pattern
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* Compression methods supported by the SDK
|
|
13
|
+
*/
|
|
14
|
+
export declare enum Compression {
|
|
15
|
+
GZipJS = "gzip-js",
|
|
16
|
+
None = "none"
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Response from a request
|
|
20
|
+
*/
|
|
21
|
+
export interface RequestResponse {
|
|
22
|
+
statusCode: number;
|
|
23
|
+
text?: string;
|
|
24
|
+
json?: any;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Options for making a request
|
|
28
|
+
*/
|
|
29
|
+
export interface RequestOptions {
|
|
30
|
+
url: string;
|
|
31
|
+
data?: any;
|
|
32
|
+
method?: "POST" | "GET";
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
transport?: "XHR" | "fetch" | "sendBeacon";
|
|
35
|
+
compression?: Compression;
|
|
36
|
+
timeout?: number;
|
|
37
|
+
callback?: (response: RequestResponse) => void;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* JSON stringify with BigInt support
|
|
41
|
+
*/
|
|
42
|
+
export declare const jsonStringify: (data: any) => string;
|
|
43
|
+
/**
|
|
44
|
+
* Main request function - handles transport selection and dispatching
|
|
45
|
+
*/
|
|
46
|
+
export declare const request: (options: RequestOptions) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Promise-based request wrapper
|
|
49
|
+
*/
|
|
50
|
+
export declare const requestAsync: (options: Omit<RequestOptions, "callback">) => Promise<RequestResponse>;
|
|
51
|
+
/**
|
|
52
|
+
* Check if compression is supported and beneficial
|
|
53
|
+
*/
|
|
54
|
+
export declare const shouldCompress: (data: any) => boolean;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Retry Queue - Exponential Backoff (PostHog-style)
|
|
3
|
+
*
|
|
4
|
+
* Retries failed requests with jittered exponential backoff.
|
|
5
|
+
* Detects online/offline status and pauses retries when offline.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Exponential backoff: 3s, 6s, 12s, 24s... up to 30 minutes
|
|
9
|
+
* - Jitter: +/- 50% to prevent thundering herd
|
|
10
|
+
* - Online/offline detection
|
|
11
|
+
* - Max 10 retries before giving up
|
|
12
|
+
* - Uses sendBeacon on page unload for final attempt
|
|
13
|
+
*/
|
|
14
|
+
import type { BatchedRequest } from "./request-queue";
|
|
15
|
+
/**
|
|
16
|
+
* Generates a jittered exponential backoff delay in milliseconds
|
|
17
|
+
*
|
|
18
|
+
* Base value is 3 seconds, doubled with each retry up to 30 minutes max.
|
|
19
|
+
* Each value has +/- 50% jitter.
|
|
20
|
+
*
|
|
21
|
+
* @param retriesPerformedSoFar - Number of retries already attempted
|
|
22
|
+
* @returns Delay in milliseconds
|
|
23
|
+
*/
|
|
24
|
+
export declare function pickNextRetryDelay(retriesPerformedSoFar: number): number;
|
|
25
|
+
export interface RetryQueueConfig {
|
|
26
|
+
sendRequest: (req: BatchedRequest) => Promise<{
|
|
27
|
+
statusCode: number;
|
|
28
|
+
}>;
|
|
29
|
+
sendBeacon: (req: BatchedRequest) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare class RetryQueue {
|
|
32
|
+
private _isPolling;
|
|
33
|
+
private _poller?;
|
|
34
|
+
private _pollIntervalMs;
|
|
35
|
+
private _queue;
|
|
36
|
+
private _areWeOnline;
|
|
37
|
+
private _sendRequest;
|
|
38
|
+
private _sendBeacon;
|
|
39
|
+
constructor(config: RetryQueueConfig);
|
|
40
|
+
/**
|
|
41
|
+
* Get current queue length
|
|
42
|
+
*/
|
|
43
|
+
get length(): number;
|
|
44
|
+
/**
|
|
45
|
+
* Enqueue a failed request for retry
|
|
46
|
+
*/
|
|
47
|
+
enqueue(request: BatchedRequest, retriesPerformedSoFar?: number): void;
|
|
48
|
+
/**
|
|
49
|
+
* Attempt to send a request with retry on failure
|
|
50
|
+
*/
|
|
51
|
+
retriableRequest(request: BatchedRequest): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Start polling for retries
|
|
54
|
+
*/
|
|
55
|
+
private _poll;
|
|
56
|
+
/**
|
|
57
|
+
* Flush ready items from the queue
|
|
58
|
+
*/
|
|
59
|
+
private _flush;
|
|
60
|
+
/**
|
|
61
|
+
* Flush all queued requests using sendBeacon on page unload
|
|
62
|
+
*/
|
|
63
|
+
unload(): void;
|
|
64
|
+
}
|
package/dist/types.d.ts
CHANGED
package/dist/user-manager.d.ts
CHANGED
|
@@ -159,4 +159,25 @@ export declare class UserManager {
|
|
|
159
159
|
* Remove cookie value
|
|
160
160
|
*/
|
|
161
161
|
private removeCookieValue;
|
|
162
|
+
/**
|
|
163
|
+
* Register a value once (only if not already set)
|
|
164
|
+
* Stores properties in localStorage only if they don't already exist
|
|
165
|
+
*/
|
|
166
|
+
private register_once;
|
|
167
|
+
/**
|
|
168
|
+
* Set initial person info
|
|
169
|
+
* Stores referrer and URL info on first visit for generating $initial_* properties
|
|
170
|
+
*/
|
|
171
|
+
set_initial_person_info(maskPersonalDataProperties?: boolean, customPersonalDataProperties?: string[]): void;
|
|
172
|
+
/**
|
|
173
|
+
* Get initial props
|
|
174
|
+
* Generates $initial_* properties from stored initial person info
|
|
175
|
+
* These are sent with events as $set_once to preserve first values
|
|
176
|
+
*/
|
|
177
|
+
get_initial_props(): Record<string, any>;
|
|
178
|
+
/**
|
|
179
|
+
* Update referrer info
|
|
180
|
+
* Stores current referrer information if not already stored
|
|
181
|
+
*/
|
|
182
|
+
update_referrer_info(): void;
|
|
162
183
|
}
|
|
@@ -1,34 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Event utilities
|
|
3
|
+
* Functions for extracting event properties, campaign parameters, and person info
|
|
4
4
|
*/
|
|
5
|
-
export declare
|
|
5
|
+
export declare const PERSONAL_DATA_CAMPAIGN_PARAMS: string[];
|
|
6
|
+
export declare const CAMPAIGN_PARAMS: string[];
|
|
7
|
+
export declare const EVENT_TO_PERSON_PROPERTIES: string[];
|
|
8
|
+
export declare const MASKED = "<masked>";
|
|
9
|
+
export declare const COOKIE_CAMPAIGN_PARAMS: string[];
|
|
6
10
|
/**
|
|
7
|
-
* Get
|
|
8
|
-
*
|
|
11
|
+
* Get campaign parameters from URL
|
|
12
|
+
* Extracts UTM and other campaign tracking parameters from current page URL
|
|
13
|
+
* Masks personal data parameters if configured
|
|
9
14
|
*/
|
|
15
|
+
export declare function getCampaignParams(customTrackedParams?: string[], maskPersonalDataProperties?: boolean, customPersonalDataProperties?: string[] | undefined): Record<string, string>;
|
|
16
|
+
export declare function getSearchInfo(): Record<string, any>;
|
|
17
|
+
export declare function getBrowserLanguage(): string | undefined;
|
|
10
18
|
export declare function getBrowserLanguagePrefix(): string | undefined;
|
|
19
|
+
export declare function getReferrer(): string;
|
|
20
|
+
export declare function getReferringDomain(): string;
|
|
11
21
|
/**
|
|
12
|
-
* Get referrer
|
|
13
|
-
* Returns
|
|
22
|
+
* Get referrer information
|
|
23
|
+
* Returns current referrer and referring domain
|
|
14
24
|
*/
|
|
15
|
-
export declare function
|
|
25
|
+
export declare function getReferrerInfo(): Record<string, any>;
|
|
16
26
|
/**
|
|
17
|
-
* Get
|
|
18
|
-
*
|
|
27
|
+
* Get person info for initial storage
|
|
28
|
+
* Extracts referrer and URL info, masks personal data if configured
|
|
29
|
+
* Returns compact format (r: referrer, u: url) for storage efficiency
|
|
19
30
|
*/
|
|
20
|
-
export declare function
|
|
31
|
+
export declare function getPersonInfo(maskPersonalDataProperties?: boolean, customPersonalDataProperties?: string[]): {
|
|
32
|
+
r: string;
|
|
33
|
+
u: string | undefined;
|
|
34
|
+
};
|
|
21
35
|
/**
|
|
22
|
-
*
|
|
23
|
-
*
|
|
36
|
+
* Convert person info to person properties
|
|
37
|
+
* Extracts referrer, URL, campaign params, and search info from stored person info
|
|
24
38
|
*/
|
|
25
|
-
export declare function
|
|
39
|
+
export declare function getPersonPropsFromInfo(info: Record<string, any>): Record<string, any>;
|
|
26
40
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
41
|
+
* Convert person info to initial person properties
|
|
42
|
+
* Generates $initial_* properties from person info (preserves first values)
|
|
29
43
|
*/
|
|
44
|
+
export declare function getInitialPersonPropsFromInfo(info: Record<string, any>): Record<string, any>;
|
|
45
|
+
export declare function getTimezone(): string | undefined;
|
|
30
46
|
export declare function getTimezoneOffset(): number | undefined;
|
|
31
47
|
/**
|
|
32
48
|
* Get event properties that should be added to all events
|
|
49
|
+
* Returns all event context properties (browser, device, URL, etc.) plus event metadata
|
|
50
|
+
* Note: Only properties in EVENT_TO_PERSON_PROPERTIES are copied to person properties
|
|
33
51
|
*/
|
|
34
|
-
export declare function getEventProperties(): Record<string, any>;
|
|
52
|
+
export declare function getEventProperties(maskPersonalDataProperties?: boolean, customPersonalDataProperties?: string[]): Record<string, any>;
|
package/dist/utils/index.d.ts
CHANGED
|
@@ -23,3 +23,24 @@ export declare function each<T>(obj: T[] | Record<string, T> | null | undefined,
|
|
|
23
23
|
* This properly implements the default options for passive event listeners
|
|
24
24
|
*/
|
|
25
25
|
export declare function addEventListener(element: Window | Document | Element | undefined, event: string, callback: EventListener, options?: AddEventListenerOptions): void;
|
|
26
|
+
/**
|
|
27
|
+
* Extend object with properties from another object
|
|
28
|
+
*/
|
|
29
|
+
export declare function extend<T extends Record<string, any>>(target: T, source: Record<string, any> | null | undefined): T;
|
|
30
|
+
/**
|
|
31
|
+
* Extend array with additional items
|
|
32
|
+
* Mutates the base array and returns it (matches PostHog's behavior)
|
|
33
|
+
*/
|
|
34
|
+
export declare function extendArray<T>(base: T[], ...additional: T[][]): T[];
|
|
35
|
+
/**
|
|
36
|
+
* Strip properties with empty values (null, undefined, empty string)
|
|
37
|
+
*/
|
|
38
|
+
export declare function stripEmptyProperties<T extends Record<string, any>>(obj: T): Partial<T>;
|
|
39
|
+
/**
|
|
40
|
+
* Strip leading dollar sign from string
|
|
41
|
+
*/
|
|
42
|
+
export declare function stripLeadingDollar(str: string): string;
|
|
43
|
+
/**
|
|
44
|
+
* Check if value is null
|
|
45
|
+
*/
|
|
46
|
+
export declare function isNull(value: any): boolean;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request utilities
|
|
3
|
+
* Functions for parsing URLs, query parameters, and masking sensitive data
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Convert string URL to HTMLAnchorElement for parsing
|
|
7
|
+
* IE11 doesn't support `new URL`, so we use anchor element
|
|
8
|
+
*/
|
|
9
|
+
export declare function convertToURL(url: string): HTMLAnchorElement | null;
|
|
10
|
+
/**
|
|
11
|
+
* Get query parameter from URL
|
|
12
|
+
*/
|
|
13
|
+
export declare function getQueryParam(url: string, param: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* Mask query parameters in URL
|
|
16
|
+
*/
|
|
17
|
+
export declare function maskQueryParams<T extends string | undefined>(url: T, maskedParams: string[] | undefined, mask: string): T extends string ? string : undefined;
|
package/dist/vtilt.d.ts
CHANGED
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { VTiltConfig, EventPayload } from "./types";
|
|
2
2
|
import { HistoryAutocapture } from "./extensions/history-autocapture";
|
|
3
|
-
|
|
4
|
-
url: string;
|
|
5
|
-
event: any;
|
|
6
|
-
}
|
|
3
|
+
import { type QueuedRequest } from "./request-queue";
|
|
7
4
|
export declare class VTilt {
|
|
8
5
|
private configManager;
|
|
9
6
|
private sessionManager;
|
|
10
7
|
private userManager;
|
|
11
8
|
private webVitalsManager;
|
|
9
|
+
private requestQueue;
|
|
10
|
+
private retryQueue;
|
|
11
|
+
private rateLimiter;
|
|
12
12
|
historyAutocapture?: HistoryAutocapture;
|
|
13
13
|
__loaded: boolean;
|
|
14
14
|
private _initialPageviewCaptured;
|
|
15
15
|
private _visibilityStateListener;
|
|
16
16
|
__request_queue: QueuedRequest[];
|
|
17
17
|
private _hasWarnedAboutConfig;
|
|
18
|
+
private _setOncePropertiesSent;
|
|
18
19
|
constructor(config?: Partial<VTiltConfig>);
|
|
19
20
|
/**
|
|
20
21
|
* Initializes a new instance of the VTilt tracking object.
|
|
@@ -52,6 +53,11 @@ export declare class VTilt {
|
|
|
52
53
|
* This internal method should only be called by `init()`.
|
|
53
54
|
*/
|
|
54
55
|
private _init;
|
|
56
|
+
/**
|
|
57
|
+
* Set up handler to flush event queue on page unload
|
|
58
|
+
* Uses both beforeunload and pagehide for maximum compatibility
|
|
59
|
+
*/
|
|
60
|
+
private _setupUnloadHandler;
|
|
55
61
|
/**
|
|
56
62
|
* Returns a string representation of the instance name
|
|
57
63
|
* Used for debugging and logging
|
|
@@ -77,8 +83,26 @@ export declare class VTilt {
|
|
|
77
83
|
/**
|
|
78
84
|
* Send HTTP request
|
|
79
85
|
* This is the central entry point for all tracking requests
|
|
86
|
+
* Events are batched and sent every 3 seconds for better performance
|
|
80
87
|
*/
|
|
81
88
|
private sendRequest;
|
|
89
|
+
/**
|
|
90
|
+
* Send a batched request with multiple events
|
|
91
|
+
* Called by RequestQueue when flushing
|
|
92
|
+
* Uses RetryQueue for automatic retry on failure
|
|
93
|
+
*/
|
|
94
|
+
private _sendBatchedRequest;
|
|
95
|
+
/**
|
|
96
|
+
* Send HTTP request and return status code
|
|
97
|
+
* Uses GZip compression for payloads > 1KB
|
|
98
|
+
* Used by RetryQueue for retryable requests
|
|
99
|
+
*/
|
|
100
|
+
private _sendHttpRequest;
|
|
101
|
+
/**
|
|
102
|
+
* Send request using sendBeacon for reliable delivery on page unload
|
|
103
|
+
* Uses GZip compression for payloads > 1KB
|
|
104
|
+
*/
|
|
105
|
+
private _sendBeaconRequest;
|
|
82
106
|
/**
|
|
83
107
|
* Send a queued request (called after DOM is loaded)
|
|
84
108
|
*/
|
|
@@ -86,13 +110,22 @@ export declare class VTilt {
|
|
|
86
110
|
/**
|
|
87
111
|
* Capture an event
|
|
88
112
|
* Automatically adds common properties to all events
|
|
89
|
-
* ($current_url, $host, $pathname, $referrer, $referring_domain, $
|
|
113
|
+
* ($current_url, $host, $pathname, $referrer, $referring_domain, $browser, $os, $device, $timezone, etc.)
|
|
114
|
+
* Only properties in EVENT_TO_PERSON_PROPERTIES are copied to person properties
|
|
90
115
|
* Also adds title property for $pageview events only
|
|
91
116
|
*
|
|
92
117
|
* @param name - Event name
|
|
93
118
|
* @param payload - Event payload
|
|
119
|
+
* @param options - Optional capture options
|
|
120
|
+
*/
|
|
121
|
+
capture(name: string, payload: EventPayload, options?: {
|
|
122
|
+
skip_client_rate_limiting?: boolean;
|
|
123
|
+
}): void;
|
|
124
|
+
/**
|
|
125
|
+
* Internal capture method that bypasses rate limiting
|
|
126
|
+
* Used for system events like rate limit warnings
|
|
94
127
|
*/
|
|
95
|
-
|
|
128
|
+
private _captureInternal;
|
|
96
129
|
/**
|
|
97
130
|
* Track a custom event (alias for capture)
|
|
98
131
|
*/
|
|
@@ -213,7 +246,7 @@ export declare class VTilt {
|
|
|
213
246
|
*/
|
|
214
247
|
_execute_array(array: any[]): void;
|
|
215
248
|
/**
|
|
216
|
-
* Called when DOM is loaded - processes queued requests
|
|
249
|
+
* Called when DOM is loaded - processes queued requests and enables batching
|
|
217
250
|
*/
|
|
218
251
|
_dom_loaded(): void;
|
|
219
252
|
}
|
|
@@ -253,4 +286,3 @@ export declare function init_as_module(): VTilt;
|
|
|
253
286
|
* ]
|
|
254
287
|
*/
|
|
255
288
|
export declare function init_from_snippet(): void;
|
|
256
|
-
export {};
|
package/lib/constants.d.ts
CHANGED
|
@@ -7,5 +7,6 @@ export declare const DISTINCT_ID_KEY = "vt_distinct_id";
|
|
|
7
7
|
export declare const DEVICE_ID_KEY = "vt_device_id";
|
|
8
8
|
export declare const USER_PROPERTIES_KEY = "vt_user_properties";
|
|
9
9
|
export declare const USER_STATE_KEY = "vt_user_state";
|
|
10
|
+
export declare const INITIAL_PERSON_INFO = "$initial_person_info";
|
|
10
11
|
export declare const STORAGE_METHODS: StorageMethods;
|
|
11
12
|
export declare const TIMEZONES: Record<string, string>;
|
package/lib/constants.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TIMEZONES = exports.STORAGE_METHODS = exports.USER_STATE_KEY = exports.USER_PROPERTIES_KEY = exports.DEVICE_ID_KEY = exports.DISTINCT_ID_KEY = exports.ANONYMOUS_ID_KEY = exports.PRIMARY_WINDOW_EXISTS_KEY = exports.WINDOW_ID_KEY = exports.STORAGE_KEY = void 0;
|
|
3
|
+
exports.TIMEZONES = exports.STORAGE_METHODS = exports.INITIAL_PERSON_INFO = exports.USER_STATE_KEY = exports.USER_PROPERTIES_KEY = exports.DEVICE_ID_KEY = exports.DISTINCT_ID_KEY = exports.ANONYMOUS_ID_KEY = exports.PRIMARY_WINDOW_EXISTS_KEY = exports.WINDOW_ID_KEY = exports.STORAGE_KEY = void 0;
|
|
4
4
|
exports.STORAGE_KEY = "vt_session_id";
|
|
5
5
|
exports.WINDOW_ID_KEY = "vt_window_id";
|
|
6
6
|
exports.PRIMARY_WINDOW_EXISTS_KEY = "vt_primary_window_exists";
|
|
@@ -9,6 +9,7 @@ exports.DISTINCT_ID_KEY = "vt_distinct_id";
|
|
|
9
9
|
exports.DEVICE_ID_KEY = "vt_device_id";
|
|
10
10
|
exports.USER_PROPERTIES_KEY = "vt_user_properties";
|
|
11
11
|
exports.USER_STATE_KEY = "vt_user_state";
|
|
12
|
+
exports.INITIAL_PERSON_INFO = "$initial_person_info";
|
|
12
13
|
exports.STORAGE_METHODS = {
|
|
13
14
|
cookie: "cookie",
|
|
14
15
|
localStorage: "localStorage",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiter - Token Bucket Algorithm (PostHog-style)
|
|
3
|
+
*
|
|
4
|
+
* Prevents runaway loops from flooding the server with events.
|
|
5
|
+
* Uses a token bucket algorithm with configurable rate and burst limits.
|
|
6
|
+
*
|
|
7
|
+
* Features:
|
|
8
|
+
* - Configurable events per second (default: 10)
|
|
9
|
+
* - Configurable burst limit (default: 100)
|
|
10
|
+
* - Token replenishment over time
|
|
11
|
+
* - Warning event when rate limited
|
|
12
|
+
*/
|
|
13
|
+
export declare const RATE_LIMIT_WARNING_EVENT = "$$client_ingestion_warning";
|
|
14
|
+
export interface RateLimitBucket {
|
|
15
|
+
tokens: number;
|
|
16
|
+
last: number;
|
|
17
|
+
}
|
|
18
|
+
export interface RateLimiterConfig {
|
|
19
|
+
eventsPerSecond?: number;
|
|
20
|
+
eventsBurstLimit?: number;
|
|
21
|
+
persistence?: {
|
|
22
|
+
get: (key: string) => RateLimitBucket | null;
|
|
23
|
+
set: (key: string, value: RateLimitBucket) => void;
|
|
24
|
+
};
|
|
25
|
+
captureWarning?: (message: string) => void;
|
|
26
|
+
}
|
|
27
|
+
export declare class RateLimiter {
|
|
28
|
+
private eventsPerSecond;
|
|
29
|
+
private eventsBurstLimit;
|
|
30
|
+
private lastEventRateLimited;
|
|
31
|
+
private persistence?;
|
|
32
|
+
private captureWarning?;
|
|
33
|
+
constructor(config?: RateLimiterConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Check if the client should be rate limited
|
|
36
|
+
*
|
|
37
|
+
* @param checkOnly - If true, don't consume a token (just check)
|
|
38
|
+
* @returns Object with isRateLimited flag and remaining tokens
|
|
39
|
+
*/
|
|
40
|
+
checkRateLimit(checkOnly?: boolean): {
|
|
41
|
+
isRateLimited: boolean;
|
|
42
|
+
remainingTokens: number;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Check if an event should be allowed (consumes a token if allowed)
|
|
46
|
+
*/
|
|
47
|
+
shouldAllowEvent(): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Get remaining tokens without consuming
|
|
50
|
+
*/
|
|
51
|
+
getRemainingTokens(): number;
|
|
52
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Rate Limiter - Token Bucket Algorithm (PostHog-style)
|
|
4
|
+
*
|
|
5
|
+
* Prevents runaway loops from flooding the server with events.
|
|
6
|
+
* Uses a token bucket algorithm with configurable rate and burst limits.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Configurable events per second (default: 10)
|
|
10
|
+
* - Configurable burst limit (default: 100)
|
|
11
|
+
* - Token replenishment over time
|
|
12
|
+
* - Warning event when rate limited
|
|
13
|
+
*/
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.RateLimiter = exports.RATE_LIMIT_WARNING_EVENT = void 0;
|
|
16
|
+
const RATE_LIMIT_STORAGE_KEY = "vt_rate_limit";
|
|
17
|
+
exports.RATE_LIMIT_WARNING_EVENT = "$$client_ingestion_warning";
|
|
18
|
+
class RateLimiter {
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
var _a, _b;
|
|
21
|
+
this.lastEventRateLimited = false;
|
|
22
|
+
this.eventsPerSecond = (_a = config.eventsPerSecond) !== null && _a !== void 0 ? _a : 10;
|
|
23
|
+
this.eventsBurstLimit = Math.max((_b = config.eventsBurstLimit) !== null && _b !== void 0 ? _b : this.eventsPerSecond * 10, this.eventsPerSecond);
|
|
24
|
+
this.persistence = config.persistence;
|
|
25
|
+
this.captureWarning = config.captureWarning;
|
|
26
|
+
// Initialize lastEventRateLimited from current state
|
|
27
|
+
this.lastEventRateLimited = this.checkRateLimit(true).isRateLimited;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Check if the client should be rate limited
|
|
31
|
+
*
|
|
32
|
+
* @param checkOnly - If true, don't consume a token (just check)
|
|
33
|
+
* @returns Object with isRateLimited flag and remaining tokens
|
|
34
|
+
*/
|
|
35
|
+
checkRateLimit(checkOnly = false) {
|
|
36
|
+
var _a, _b, _c, _d;
|
|
37
|
+
const now = Date.now();
|
|
38
|
+
// Get current bucket state from persistence or create new
|
|
39
|
+
const bucket = (_b = (_a = this.persistence) === null || _a === void 0 ? void 0 : _a.get(RATE_LIMIT_STORAGE_KEY)) !== null && _b !== void 0 ? _b : {
|
|
40
|
+
tokens: this.eventsBurstLimit,
|
|
41
|
+
last: now,
|
|
42
|
+
};
|
|
43
|
+
// Replenish tokens based on time elapsed
|
|
44
|
+
const secondsElapsed = (now - bucket.last) / 1000;
|
|
45
|
+
bucket.tokens += secondsElapsed * this.eventsPerSecond;
|
|
46
|
+
bucket.last = now;
|
|
47
|
+
// Cap tokens at burst limit
|
|
48
|
+
if (bucket.tokens > this.eventsBurstLimit) {
|
|
49
|
+
bucket.tokens = this.eventsBurstLimit;
|
|
50
|
+
}
|
|
51
|
+
const isRateLimited = bucket.tokens < 1;
|
|
52
|
+
// Consume a token if not just checking
|
|
53
|
+
if (!isRateLimited && !checkOnly) {
|
|
54
|
+
bucket.tokens = Math.max(0, bucket.tokens - 1);
|
|
55
|
+
}
|
|
56
|
+
// Capture warning event when first rate limited
|
|
57
|
+
if (isRateLimited && !this.lastEventRateLimited && !checkOnly) {
|
|
58
|
+
(_c = this.captureWarning) === null || _c === void 0 ? void 0 : _c.call(this, `vTilt client rate limited. Config: ${this.eventsPerSecond} events/second, ${this.eventsBurstLimit} burst limit.`);
|
|
59
|
+
}
|
|
60
|
+
this.lastEventRateLimited = isRateLimited;
|
|
61
|
+
(_d = this.persistence) === null || _d === void 0 ? void 0 : _d.set(RATE_LIMIT_STORAGE_KEY, bucket);
|
|
62
|
+
return {
|
|
63
|
+
isRateLimited,
|
|
64
|
+
remainingTokens: bucket.tokens,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Check if an event should be allowed (consumes a token if allowed)
|
|
69
|
+
*/
|
|
70
|
+
shouldAllowEvent() {
|
|
71
|
+
return !this.checkRateLimit(false).isRateLimited;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Get remaining tokens without consuming
|
|
75
|
+
*/
|
|
76
|
+
getRemainingTokens() {
|
|
77
|
+
return this.checkRateLimit(true).remainingTokens;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
exports.RateLimiter = RateLimiter;
|