@ordersune/crm-web-sdk 1.0.6 → 1.0.8
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/types/index.d.ts +72 -2
- package/dist/web-sdk.d.ts +20 -3
- package/dist/web-sdk.js +464 -70
- package/package.json +1 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
export interface UserProfile {
|
|
2
2
|
id: string;
|
|
3
|
-
user_type: string;
|
|
4
|
-
traits: Record<string, any>;
|
|
5
3
|
}
|
|
6
4
|
export interface EventOptions {
|
|
7
5
|
timestamp?: string;
|
|
@@ -11,4 +9,76 @@ export interface SDKConfig {
|
|
|
11
9
|
apiKey: string;
|
|
12
10
|
endpoint: string;
|
|
13
11
|
debug?: boolean;
|
|
12
|
+
batchSize?: number;
|
|
13
|
+
batchInterval?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface PurchasePayload {
|
|
16
|
+
externalId: string;
|
|
17
|
+
itemId: string;
|
|
18
|
+
itemName: string;
|
|
19
|
+
unitPrice: number;
|
|
20
|
+
quantity: number;
|
|
21
|
+
source: string;
|
|
22
|
+
channel: string;
|
|
23
|
+
itemCategory?: string;
|
|
24
|
+
}
|
|
25
|
+
export interface DeviceDetails {
|
|
26
|
+
deviceId: string;
|
|
27
|
+
deviceModel: string;
|
|
28
|
+
browser: string;
|
|
29
|
+
browserVersion: string;
|
|
30
|
+
language: string;
|
|
31
|
+
screenResolution: string;
|
|
32
|
+
userAgent: string;
|
|
33
|
+
platform: string;
|
|
34
|
+
pushToken?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface BrowserInfo {
|
|
37
|
+
browserType: string;
|
|
38
|
+
browserVersion: string;
|
|
39
|
+
}
|
|
40
|
+
export interface ButtonProperties {
|
|
41
|
+
backgroundColor: string;
|
|
42
|
+
borderColor: string;
|
|
43
|
+
textColor: string;
|
|
44
|
+
androidOnClickValue?: string;
|
|
45
|
+
androidOnClickAction?: string;
|
|
46
|
+
iOSOnClickValue?: string;
|
|
47
|
+
iOSOnClickAction?: string;
|
|
48
|
+
type?: string;
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
}
|
|
51
|
+
export interface ModalConfig {
|
|
52
|
+
button1: ButtonProperties;
|
|
53
|
+
button2: ButtonProperties;
|
|
54
|
+
dismissMessageAutomatically: boolean;
|
|
55
|
+
dismissMessageInSeconds: string;
|
|
56
|
+
headerColor: string;
|
|
57
|
+
messageColor: string;
|
|
58
|
+
closeColor: string;
|
|
59
|
+
}
|
|
60
|
+
export interface MediaConfig {
|
|
61
|
+
inAppImage: string;
|
|
62
|
+
}
|
|
63
|
+
export interface InAppMessageData {
|
|
64
|
+
header: string;
|
|
65
|
+
message: string;
|
|
66
|
+
modal: ModalConfig;
|
|
67
|
+
mediaConfig: MediaConfig;
|
|
68
|
+
}
|
|
69
|
+
export interface ShowInAppMessageOptions {
|
|
70
|
+
context?: any;
|
|
71
|
+
title: string;
|
|
72
|
+
body: string;
|
|
73
|
+
primaryButtonText: string;
|
|
74
|
+
secondaryButtonText?: string;
|
|
75
|
+
imageUrl: string;
|
|
76
|
+
onClose?: () => void;
|
|
77
|
+
buttonPropertiesPrimary: ButtonProperties;
|
|
78
|
+
buttonPropertiesSecondary: ButtonProperties;
|
|
79
|
+
dismissMessageAutomatically: boolean;
|
|
80
|
+
dismissMessageInSeconds: string;
|
|
81
|
+
headerColor: string;
|
|
82
|
+
messageColor: string;
|
|
83
|
+
closeColor: string;
|
|
14
84
|
}
|
package/dist/web-sdk.d.ts
CHANGED
|
@@ -10,18 +10,31 @@ export declare class WebSDK {
|
|
|
10
10
|
private deviceDetails?;
|
|
11
11
|
private currentUser?;
|
|
12
12
|
private eventQueue;
|
|
13
|
+
private purchaseBatch;
|
|
14
|
+
private attributeQueue;
|
|
13
15
|
private isProcessingQueue;
|
|
14
|
-
private
|
|
16
|
+
private batchProcessingInterval?;
|
|
15
17
|
constructor(config: SDKConfig);
|
|
16
|
-
|
|
18
|
+
private initialize;
|
|
19
|
+
identify(userId: string): void;
|
|
20
|
+
clearUserData(): void;
|
|
17
21
|
trackEvent(eventName: string, properties?: Record<string, any>, options?: EventOptions): void;
|
|
22
|
+
setToken(token: string): void;
|
|
18
23
|
trackCustomAttribute(attributeName: string, attributeValue: any): void;
|
|
24
|
+
logProductPurchase(orderId: string, itemId: string, itemName: string, unitPrice: number, quantity: number, source: string, channel: string, itemCategory?: string): void;
|
|
19
25
|
getUserProfile(): UserProfile | undefined;
|
|
20
|
-
|
|
26
|
+
forceSend(): Promise<void>;
|
|
27
|
+
dispose(): void;
|
|
21
28
|
private getStoredDeviceId;
|
|
29
|
+
private getStoredUserProfile;
|
|
22
30
|
private storeDeviceId;
|
|
31
|
+
private storeUserProfile;
|
|
32
|
+
private storeCustomAttributes;
|
|
33
|
+
private loadCustomAttributes;
|
|
23
34
|
private getGuestUserId;
|
|
35
|
+
private buildPurchasePayload;
|
|
24
36
|
private initializeDeviceDetails;
|
|
37
|
+
private getBrowserInfo;
|
|
25
38
|
private detectDeviceType;
|
|
26
39
|
private detectBrowserType;
|
|
27
40
|
private detectBrowserVersion;
|
|
@@ -29,4 +42,8 @@ export declare class WebSDK {
|
|
|
29
42
|
private queueEvent;
|
|
30
43
|
private processBatch;
|
|
31
44
|
private sendBatchToServer;
|
|
45
|
+
displayInAppMessage(inAppMessageData: string, context?: any): void;
|
|
46
|
+
private hexToColor;
|
|
47
|
+
private openWebUrl;
|
|
48
|
+
private showInAppMessage;
|
|
32
49
|
}
|
package/dist/web-sdk.js
CHANGED
|
@@ -6,76 +6,232 @@ const uuid_1 = require("uuid");
|
|
|
6
6
|
const STORAGE_PREFIX = "os.";
|
|
7
7
|
const DEFAULT_BATCH_SIZE = 10;
|
|
8
8
|
const DEFAULT_BATCH_INTERVAL = 10000;
|
|
9
|
-
const SDK_VERSION = "1.0.
|
|
9
|
+
const SDK_VERSION = "1.0.7";
|
|
10
10
|
class WebSDK {
|
|
11
11
|
constructor(config) {
|
|
12
|
-
var _a;
|
|
12
|
+
var _a, _b, _c;
|
|
13
13
|
this.maxBatchSize = DEFAULT_BATCH_SIZE;
|
|
14
14
|
this.batchInterval = DEFAULT_BATCH_INTERVAL;
|
|
15
15
|
this.sdkVersion = SDK_VERSION;
|
|
16
16
|
this.eventQueue = [];
|
|
17
|
+
this.purchaseBatch = [];
|
|
18
|
+
this.attributeQueue = {};
|
|
17
19
|
this.isProcessingQueue = false;
|
|
18
|
-
|
|
20
|
+
if (!config.apiKey || !config.endpoint) {
|
|
21
|
+
throw new Error("API key and endpoint are required");
|
|
22
|
+
}
|
|
19
23
|
this.apiKey = config.apiKey;
|
|
20
24
|
this.endpoint = config.endpoint;
|
|
21
25
|
this.debug = (_a = config.debug) !== null && _a !== void 0 ? _a : false;
|
|
26
|
+
this.maxBatchSize = (_b = config.batchSize) !== null && _b !== void 0 ? _b : DEFAULT_BATCH_SIZE;
|
|
27
|
+
this.batchInterval = (_c = config.batchInterval) !== null && _c !== void 0 ? _c : DEFAULT_BATCH_INTERVAL;
|
|
22
28
|
this.deviceId = this.getStoredDeviceId() || (0, uuid_1.v4)();
|
|
23
|
-
|
|
29
|
+
// Initialize async
|
|
30
|
+
this.initialize().catch((error) => {
|
|
31
|
+
(0, utils_1.log)(this.debug, "Async initialization failed", error);
|
|
32
|
+
});
|
|
24
33
|
}
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
async initialize() {
|
|
35
|
+
try {
|
|
36
|
+
(0, utils_1.log)(this.debug, "SDK Initialized");
|
|
37
|
+
this.storeDeviceId();
|
|
38
|
+
await this.initializeDeviceDetails();
|
|
39
|
+
await this.loadCustomAttributes();
|
|
40
|
+
const storedUser = this.getStoredUserProfile();
|
|
41
|
+
if (storedUser) {
|
|
42
|
+
this.currentUser = storedUser;
|
|
43
|
+
(0, utils_1.log)(this.debug, "Loaded user profile", this.currentUser);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
this.identify("");
|
|
47
|
+
}
|
|
48
|
+
try {
|
|
49
|
+
const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
50
|
+
this.trackCustomAttribute("timezone", timezone);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
(0, utils_1.log)(this.debug, "Failed to detect timezone", error);
|
|
54
|
+
}
|
|
55
|
+
this.startBatchProcessing();
|
|
56
|
+
this.trackEvent("app_started", {});
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
(0, utils_1.log)(this.debug, "Initialization failed", error);
|
|
60
|
+
throw error;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
identify(userId) {
|
|
64
|
+
if (typeof userId !== "string") {
|
|
65
|
+
throw new Error("User ID must be a string");
|
|
66
|
+
}
|
|
67
|
+
const storedUser = this.getStoredUserProfile();
|
|
68
|
+
if ((storedUser === null || storedUser === void 0 ? void 0 : storedUser.id) === userId)
|
|
69
|
+
return;
|
|
70
|
+
(0, utils_1.log)(this.debug, "Identifying user", userId);
|
|
27
71
|
this.currentUser = {
|
|
28
72
|
id: userId || this.getGuestUserId(),
|
|
29
|
-
user_type: userId ? "registered" : "guest",
|
|
30
|
-
traits: { ...(_a = this.currentUser) === null || _a === void 0 ? void 0 : _a.traits, ...traits },
|
|
31
73
|
};
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
|
|
74
|
+
this.storeUserProfile();
|
|
75
|
+
}
|
|
76
|
+
clearUserData() {
|
|
77
|
+
this.currentUser = {
|
|
78
|
+
id: this.getGuestUserId(),
|
|
79
|
+
};
|
|
80
|
+
this.attributeQueue = {};
|
|
81
|
+
this.eventQueue = [];
|
|
82
|
+
this.purchaseBatch = [];
|
|
83
|
+
sessionStorage.setItem(`${STORAGE_PREFIX}${this.apiKey}.current_user`, JSON.stringify(this.currentUser));
|
|
84
|
+
sessionStorage.removeItem(`${STORAGE_PREFIX}${this.apiKey}.custom_attributes`);
|
|
85
|
+
this.trackEvent("user_logged_out", {
|
|
86
|
+
device_id: this.deviceId,
|
|
35
87
|
});
|
|
88
|
+
if (this.debug) {
|
|
89
|
+
(0, utils_1.log)(this.debug, "User data cleared");
|
|
90
|
+
}
|
|
36
91
|
}
|
|
37
92
|
trackEvent(eventName, properties = {}, options = {}) {
|
|
93
|
+
if (!eventName || typeof eventName !== "string") {
|
|
94
|
+
throw new Error("Event name is required and must be a string");
|
|
95
|
+
}
|
|
38
96
|
const payload = {
|
|
39
97
|
deviceId: this.deviceId,
|
|
40
98
|
eventName,
|
|
41
99
|
timestamp: options.timestamp || Date.now(),
|
|
42
100
|
requestId: options.requestId || (0, uuid_1.v4)(),
|
|
43
|
-
properties,
|
|
101
|
+
properties: properties,
|
|
44
102
|
};
|
|
45
103
|
this.queueEvent(payload);
|
|
46
104
|
}
|
|
105
|
+
setToken(token) {
|
|
106
|
+
this.deviceDetails.pushToken = token;
|
|
107
|
+
}
|
|
47
108
|
trackCustomAttribute(attributeName, attributeValue) {
|
|
48
|
-
|
|
109
|
+
if (!attributeName || typeof attributeName !== "string") {
|
|
110
|
+
throw new Error("Attribute name is required and must be a string");
|
|
111
|
+
}
|
|
112
|
+
this.attributeQueue = {
|
|
113
|
+
...this.attributeQueue,
|
|
114
|
+
[attributeName]: attributeValue,
|
|
115
|
+
};
|
|
116
|
+
(0, utils_1.log)(this.debug, "Custom attribute queued", attributeName, attributeValue);
|
|
117
|
+
this.storeCustomAttributes();
|
|
118
|
+
}
|
|
119
|
+
logProductPurchase(orderId, itemId, itemName, unitPrice, quantity, source, channel, itemCategory) {
|
|
120
|
+
const purchasePayload = this.buildPurchasePayload({
|
|
121
|
+
externalId: orderId,
|
|
122
|
+
itemId,
|
|
123
|
+
itemName,
|
|
124
|
+
unitPrice,
|
|
125
|
+
quantity,
|
|
126
|
+
source,
|
|
127
|
+
channel,
|
|
128
|
+
itemCategory,
|
|
129
|
+
});
|
|
130
|
+
this.purchaseBatch.push(purchasePayload);
|
|
131
|
+
if (this.debug) {
|
|
132
|
+
(0, utils_1.log)(this.debug, "Purchase event queued", purchasePayload);
|
|
133
|
+
}
|
|
49
134
|
}
|
|
50
135
|
getUserProfile() {
|
|
51
136
|
return this.currentUser;
|
|
52
137
|
}
|
|
53
|
-
|
|
54
|
-
this.
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
138
|
+
async forceSend() {
|
|
139
|
+
if (this.eventQueue.length > 0 ||
|
|
140
|
+
this.purchaseBatch.length > 0 ||
|
|
141
|
+
this.attributeQueue.length > 0) {
|
|
142
|
+
await this.processBatch();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
dispose() {
|
|
146
|
+
if (this.batchProcessingInterval) {
|
|
147
|
+
clearInterval(this.batchProcessingInterval);
|
|
148
|
+
}
|
|
149
|
+
if (this.eventQueue.length > 0 ||
|
|
150
|
+
this.purchaseBatch.length > 0 ||
|
|
151
|
+
this.attributeQueue.length > 0) {
|
|
152
|
+
this.processBatch().catch((error) => {
|
|
153
|
+
(0, utils_1.log)(this.debug, "Error during disposal", error);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
59
156
|
}
|
|
60
157
|
getStoredDeviceId() {
|
|
61
158
|
return localStorage.getItem(`${STORAGE_PREFIX}${this.apiKey}.device_id`);
|
|
62
159
|
}
|
|
160
|
+
getStoredUserProfile() {
|
|
161
|
+
const storedUser = sessionStorage.getItem(`${STORAGE_PREFIX}${this.apiKey}.current_user`);
|
|
162
|
+
return storedUser ? JSON.parse(storedUser) : null;
|
|
163
|
+
}
|
|
63
164
|
storeDeviceId() {
|
|
64
165
|
localStorage.setItem(`${STORAGE_PREFIX}${this.apiKey}.device_id`, this.deviceId);
|
|
65
166
|
}
|
|
167
|
+
storeUserProfile() {
|
|
168
|
+
if (this.currentUser) {
|
|
169
|
+
sessionStorage.setItem(`${STORAGE_PREFIX}${this.apiKey}.current_user`, JSON.stringify(this.currentUser));
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
storeCustomAttributes() {
|
|
173
|
+
const existingAttributes = sessionStorage.getItem(`${STORAGE_PREFIX}${this.apiKey}.custom_attributes`);
|
|
174
|
+
const existingParsed = existingAttributes
|
|
175
|
+
? JSON.parse(existingAttributes)
|
|
176
|
+
: {};
|
|
177
|
+
const mergedAttributes = {
|
|
178
|
+
...existingParsed,
|
|
179
|
+
...this.attributeQueue,
|
|
180
|
+
};
|
|
181
|
+
sessionStorage.setItem(`${STORAGE_PREFIX}${this.apiKey}.custom_attributes`, JSON.stringify(mergedAttributes));
|
|
182
|
+
}
|
|
183
|
+
async loadCustomAttributes() {
|
|
184
|
+
const stored = sessionStorage.getItem(`${STORAGE_PREFIX}${this.apiKey}.custom_attributes`);
|
|
185
|
+
if (stored) {
|
|
186
|
+
this.attributeQueue = {
|
|
187
|
+
...this.attributeQueue,
|
|
188
|
+
...JSON.parse(stored),
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
66
192
|
getGuestUserId() {
|
|
67
193
|
return `guest_user_${this.deviceId}`;
|
|
68
194
|
}
|
|
195
|
+
buildPurchasePayload({ externalId, itemId, itemName, unitPrice, quantity, source, channel, itemCategory, }) {
|
|
196
|
+
const timestamp = Date.now();
|
|
197
|
+
return {
|
|
198
|
+
external_id: externalId,
|
|
199
|
+
item_id: itemId,
|
|
200
|
+
item_name: itemName,
|
|
201
|
+
item_category: itemCategory,
|
|
202
|
+
quantity: quantity,
|
|
203
|
+
unit_price: unitPrice,
|
|
204
|
+
total_price: unitPrice * quantity,
|
|
205
|
+
source: source,
|
|
206
|
+
channel: channel,
|
|
207
|
+
purchased_at: timestamp,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
69
210
|
async initializeDeviceDetails() {
|
|
211
|
+
const deviceType = await this.detectDeviceType();
|
|
212
|
+
const browserInfo = await this.getBrowserInfo();
|
|
70
213
|
this.deviceDetails = {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
214
|
+
deviceId: this.deviceId,
|
|
215
|
+
deviceModel: deviceType,
|
|
216
|
+
browser: browserInfo.browserType,
|
|
217
|
+
browserVersion: browserInfo.browserVersion,
|
|
218
|
+
language: navigator.language,
|
|
219
|
+
screenResolution: `${window.screen.width}x${window.screen.height}`,
|
|
220
|
+
userAgent: navigator.userAgent,
|
|
221
|
+
platform: 'web',
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
async getBrowserInfo() {
|
|
225
|
+
const browserType = this.detectBrowserType();
|
|
226
|
+
const browserVersion = this.detectBrowserVersion();
|
|
227
|
+
return {
|
|
228
|
+
browserType,
|
|
229
|
+
browserVersion,
|
|
74
230
|
};
|
|
75
231
|
}
|
|
76
232
|
async detectDeviceType() {
|
|
77
|
-
|
|
78
|
-
|
|
233
|
+
try {
|
|
234
|
+
if ("userAgentData" in navigator) {
|
|
79
235
|
const data = await navigator.userAgentData.getHighEntropyValues(["platform", "mobile"]);
|
|
80
236
|
if (data.mobile) {
|
|
81
237
|
return data.platform.toLowerCase().includes("ios")
|
|
@@ -90,9 +246,9 @@ class WebSDK {
|
|
|
90
246
|
if (platform.includes("linux"))
|
|
91
247
|
return "linux";
|
|
92
248
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
(0, utils_1.log)(this.debug, "Client Hints API failed", error);
|
|
96
252
|
}
|
|
97
253
|
const userAgent = navigator.userAgent.toLowerCase();
|
|
98
254
|
if (/iphone|ipad|ipod/.test(userAgent))
|
|
@@ -108,58 +264,64 @@ class WebSDK {
|
|
|
108
264
|
return "unknown";
|
|
109
265
|
}
|
|
110
266
|
detectBrowserType() {
|
|
111
|
-
|
|
112
|
-
|
|
267
|
+
try {
|
|
268
|
+
if ("userAgentData" in navigator) {
|
|
113
269
|
const brands = navigator.userAgentData.brands;
|
|
114
270
|
const brand = brands.find((b) => !["Chrome HTML", "Chromium", "Not A(Brand"].includes(b.brand));
|
|
115
271
|
if (brand)
|
|
116
272
|
return brand.brand.toLowerCase();
|
|
117
273
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
274
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
275
|
+
if (/edg\//.test(userAgent))
|
|
276
|
+
return "edge";
|
|
277
|
+
if (/chrome/.test(userAgent) && !/edg\//.test(userAgent))
|
|
278
|
+
return "chrome";
|
|
279
|
+
if (/firefox/.test(userAgent))
|
|
280
|
+
return "firefox";
|
|
281
|
+
if (/safari/.test(userAgent) && !/chrome/.test(userAgent))
|
|
282
|
+
return "safari";
|
|
283
|
+
return "unknown";
|
|
284
|
+
}
|
|
285
|
+
catch (error) {
|
|
286
|
+
(0, utils_1.log)(this.debug, "Browser detection failed", error);
|
|
287
|
+
return "unknown";
|
|
121
288
|
}
|
|
122
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
123
|
-
if (/edg\//.test(userAgent))
|
|
124
|
-
return "edge";
|
|
125
|
-
if (/chrome/.test(userAgent) && !/edg\//.test(userAgent))
|
|
126
|
-
return "chrome";
|
|
127
|
-
if (/firefox/.test(userAgent))
|
|
128
|
-
return "firefox";
|
|
129
|
-
if (/safari/.test(userAgent) && !/chrome/.test(userAgent))
|
|
130
|
-
return "safari";
|
|
131
|
-
return "unknown";
|
|
132
289
|
}
|
|
133
290
|
detectBrowserVersion() {
|
|
134
|
-
|
|
135
|
-
|
|
291
|
+
try {
|
|
292
|
+
if ("userAgentData" in navigator) {
|
|
136
293
|
const brands = navigator.userAgentData.brands;
|
|
137
294
|
const brand = brands.find((b) => !["Chrome HTML", "Chromium", "Not A(Brand"].includes(b.brand));
|
|
138
295
|
if (brand)
|
|
139
296
|
return brand.version;
|
|
140
297
|
}
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
298
|
+
const userAgent = navigator.userAgent.toLowerCase();
|
|
299
|
+
const browserPatterns = {
|
|
300
|
+
chrome: /chrome\/(\d+)/,
|
|
301
|
+
firefox: /firefox\/(\d+)/,
|
|
302
|
+
safari: /version\/(\d+)/,
|
|
303
|
+
edge: /edg\/(\d+)/,
|
|
304
|
+
};
|
|
305
|
+
const browserType = this.detectBrowserType();
|
|
306
|
+
const pattern = browserPatterns[browserType];
|
|
307
|
+
if (!pattern)
|
|
308
|
+
return "unknown";
|
|
309
|
+
const match = userAgent.match(pattern);
|
|
310
|
+
return match ? match[1] : "unknown";
|
|
144
311
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
chrome: /chrome\/(\d+)/,
|
|
148
|
-
firefox: /firefox\/(\d+)/,
|
|
149
|
-
safari: /version\/(\d+)/,
|
|
150
|
-
edge: /edg\/(\d+)/,
|
|
151
|
-
};
|
|
152
|
-
const browserType = this.detectBrowserType();
|
|
153
|
-
const pattern = browserPatterns[browserType];
|
|
154
|
-
if (!pattern)
|
|
312
|
+
catch (error) {
|
|
313
|
+
(0, utils_1.log)(this.debug, "Browser version detection failed", error);
|
|
155
314
|
return "unknown";
|
|
156
|
-
|
|
157
|
-
return match ? match[1] : "unknown";
|
|
315
|
+
}
|
|
158
316
|
}
|
|
159
317
|
startBatchProcessing() {
|
|
160
|
-
setInterval(() => {
|
|
161
|
-
if (this.eventQueue.length > 0
|
|
162
|
-
this.
|
|
318
|
+
this.batchProcessingInterval = setInterval(() => {
|
|
319
|
+
if (this.eventQueue.length > 0 ||
|
|
320
|
+
this.purchaseBatch.length > 0 ||
|
|
321
|
+
this.attributeQueue.length > 0) {
|
|
322
|
+
this.processBatch().catch((error) => {
|
|
323
|
+
(0, utils_1.log)(this.debug, "Batch processing failed", error);
|
|
324
|
+
});
|
|
163
325
|
}
|
|
164
326
|
}, this.batchInterval);
|
|
165
327
|
}
|
|
@@ -167,33 +329,48 @@ class WebSDK {
|
|
|
167
329
|
this.eventQueue.push(payload);
|
|
168
330
|
(0, utils_1.log)(this.debug, "Event queued", payload);
|
|
169
331
|
if (this.eventQueue.length >= this.maxBatchSize) {
|
|
170
|
-
this.processBatch()
|
|
332
|
+
this.processBatch().catch((error) => {
|
|
333
|
+
(0, utils_1.log)(this.debug, "Batch processing failed", error);
|
|
334
|
+
});
|
|
171
335
|
}
|
|
172
336
|
}
|
|
173
337
|
async processBatch() {
|
|
174
338
|
if (this.isProcessingQueue)
|
|
175
339
|
return;
|
|
176
340
|
this.isProcessingQueue = true;
|
|
177
|
-
const
|
|
341
|
+
const eventsToSend = [...this.eventQueue];
|
|
342
|
+
const purchasesToSend = [...this.purchaseBatch];
|
|
343
|
+
const attributesToSend = { ...this.attributeQueue };
|
|
344
|
+
this.eventQueue = [];
|
|
345
|
+
this.purchaseBatch = [];
|
|
346
|
+
this.attributeQueue = {};
|
|
178
347
|
try {
|
|
179
|
-
await this.sendBatchToServer(
|
|
348
|
+
await this.sendBatchToServer(eventsToSend, purchasesToSend, attributesToSend);
|
|
180
349
|
}
|
|
181
350
|
catch (error) {
|
|
182
|
-
this.eventQueue.unshift(...
|
|
351
|
+
this.eventQueue.unshift(...eventsToSend);
|
|
352
|
+
this.purchaseBatch.unshift(...purchasesToSend);
|
|
353
|
+
this.attributeQueue = { ...this.attributeQueue, ...attributesToSend };
|
|
183
354
|
(0, utils_1.log)(this.debug, "Batch processing failed", error);
|
|
355
|
+
throw error;
|
|
184
356
|
}
|
|
185
357
|
finally {
|
|
186
358
|
this.isProcessingQueue = false;
|
|
187
359
|
}
|
|
188
360
|
}
|
|
189
|
-
async sendBatchToServer(
|
|
361
|
+
async sendBatchToServer(events, purchases, currentAttributes) {
|
|
190
362
|
var _a;
|
|
363
|
+
if (!this.deviceDetails) {
|
|
364
|
+
await this.initializeDeviceDetails();
|
|
365
|
+
}
|
|
191
366
|
const payload = {
|
|
192
367
|
sdkVersion: this.sdkVersion,
|
|
193
368
|
deviceInfo: this.deviceDetails,
|
|
194
369
|
userId: (_a = this.currentUser) === null || _a === void 0 ? void 0 : _a.id,
|
|
195
|
-
|
|
196
|
-
|
|
370
|
+
time: Date.now(),
|
|
371
|
+
events: events,
|
|
372
|
+
purchases: purchases,
|
|
373
|
+
userAttributes: currentAttributes,
|
|
197
374
|
};
|
|
198
375
|
try {
|
|
199
376
|
const response = await fetch(`${this.endpoint}/analytics/data`, {
|
|
@@ -207,12 +384,229 @@ class WebSDK {
|
|
|
207
384
|
if (!response.ok) {
|
|
208
385
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
209
386
|
}
|
|
387
|
+
if (this.debug) {
|
|
388
|
+
(0, utils_1.log)(this.debug, `Batch dispatched successfully: ${payload.events.length} events, ${payload.purchases.length} purchases, ${Object.keys(payload.userAttributes).length} attributes`);
|
|
389
|
+
}
|
|
210
390
|
await response.json();
|
|
391
|
+
if (Object.keys(currentAttributes).length > 0) {
|
|
392
|
+
this.attributeQueue = {};
|
|
393
|
+
sessionStorage.removeItem(`${STORAGE_PREFIX}${this.apiKey}.custom_attributes`);
|
|
394
|
+
}
|
|
211
395
|
}
|
|
212
396
|
catch (error) {
|
|
213
|
-
(0, utils_1.log)(this.debug, "Failed to send
|
|
397
|
+
(0, utils_1.log)(this.debug, "Failed to send payload to server", error);
|
|
214
398
|
throw error;
|
|
215
399
|
}
|
|
216
400
|
}
|
|
401
|
+
displayInAppMessage(inAppMessageData, context) {
|
|
402
|
+
const transformedData = JSON.parse(inAppMessageData);
|
|
403
|
+
const title = transformedData.header;
|
|
404
|
+
const body = transformedData.message;
|
|
405
|
+
const buttonDataPrimary = transformedData.modal.button1;
|
|
406
|
+
const buttonDataSecondary = transformedData.modal.button2;
|
|
407
|
+
const buttonTextPrimary = buttonDataPrimary.type || "";
|
|
408
|
+
const buttonTextSecondary = buttonDataSecondary.type || "";
|
|
409
|
+
const imageUrl = transformedData.mediaConfig.inAppImage;
|
|
410
|
+
this.showInAppMessage({
|
|
411
|
+
context,
|
|
412
|
+
title,
|
|
413
|
+
body,
|
|
414
|
+
primaryButtonText: buttonTextPrimary,
|
|
415
|
+
secondaryButtonText: buttonTextSecondary,
|
|
416
|
+
imageUrl,
|
|
417
|
+
buttonPropertiesPrimary: buttonDataPrimary,
|
|
418
|
+
buttonPropertiesSecondary: buttonDataSecondary,
|
|
419
|
+
dismissMessageAutomatically: transformedData.modal.dismissMessageAutomatically,
|
|
420
|
+
dismissMessageInSeconds: transformedData.modal.dismissMessageInSeconds,
|
|
421
|
+
headerColor: transformedData.modal.headerColor,
|
|
422
|
+
messageColor: transformedData.modal.messageColor,
|
|
423
|
+
closeColor: transformedData.modal.closeColor
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
hexToColor(hexColor) {
|
|
427
|
+
hexColor = hexColor.toUpperCase().replace("#", "");
|
|
428
|
+
if (hexColor.length === 6) {
|
|
429
|
+
return `#${hexColor}`;
|
|
430
|
+
}
|
|
431
|
+
return `#${hexColor.substring(2)}`;
|
|
432
|
+
}
|
|
433
|
+
openWebUrl(url) {
|
|
434
|
+
if (url) {
|
|
435
|
+
window.open(url, '_blank');
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
showInAppMessage(options) {
|
|
439
|
+
const { title, body, primaryButtonText, secondaryButtonText, imageUrl, onClose, buttonPropertiesPrimary, buttonPropertiesSecondary, dismissMessageAutomatically, dismissMessageInSeconds, headerColor, messageColor, closeColor } = options;
|
|
440
|
+
// Create modal container
|
|
441
|
+
const modalOverlay = document.createElement('div');
|
|
442
|
+
modalOverlay.style.position = 'fixed';
|
|
443
|
+
modalOverlay.style.top = '0';
|
|
444
|
+
modalOverlay.style.left = '0';
|
|
445
|
+
modalOverlay.style.width = '100%';
|
|
446
|
+
modalOverlay.style.height = '100%';
|
|
447
|
+
modalOverlay.style.backgroundColor = 'rgba(0, 0, 0, 0.5)';
|
|
448
|
+
modalOverlay.style.display = 'flex';
|
|
449
|
+
modalOverlay.style.justifyContent = 'center';
|
|
450
|
+
modalOverlay.style.alignItems = 'center';
|
|
451
|
+
modalOverlay.style.zIndex = '9999';
|
|
452
|
+
// Create modal dialog
|
|
453
|
+
const modalDialog = document.createElement('div');
|
|
454
|
+
modalDialog.style.backgroundColor = 'white';
|
|
455
|
+
modalDialog.style.borderRadius = '12px';
|
|
456
|
+
modalDialog.style.padding = '16px';
|
|
457
|
+
modalDialog.style.maxWidth = '400px';
|
|
458
|
+
modalDialog.style.width = '90%';
|
|
459
|
+
modalDialog.style.boxShadow = '0 4px 6px rgba(0, 0, 0, 0.1)';
|
|
460
|
+
modalDialog.style.position = 'relative';
|
|
461
|
+
// Close button
|
|
462
|
+
const closeButton = document.createElement('button');
|
|
463
|
+
closeButton.innerHTML = '×';
|
|
464
|
+
closeButton.style.position = 'absolute';
|
|
465
|
+
closeButton.style.top = '8px';
|
|
466
|
+
closeButton.style.right = '8px';
|
|
467
|
+
closeButton.style.background = 'none';
|
|
468
|
+
closeButton.style.border = 'none';
|
|
469
|
+
closeButton.style.fontSize = '20px';
|
|
470
|
+
closeButton.style.cursor = 'pointer';
|
|
471
|
+
closeButton.style.color = this.hexToColor(closeColor);
|
|
472
|
+
closeButton.onclick = () => {
|
|
473
|
+
if (onClose) {
|
|
474
|
+
onClose();
|
|
475
|
+
}
|
|
476
|
+
document.body.removeChild(modalOverlay);
|
|
477
|
+
};
|
|
478
|
+
// Create content
|
|
479
|
+
const contentContainer = document.createElement('div');
|
|
480
|
+
contentContainer.style.display = 'flex';
|
|
481
|
+
contentContainer.style.flexDirection = 'column';
|
|
482
|
+
contentContainer.style.alignItems = 'center';
|
|
483
|
+
contentContainer.style.textAlign = 'center';
|
|
484
|
+
// Image
|
|
485
|
+
if (imageUrl && imageUrl.trim() !== '') {
|
|
486
|
+
const image = document.createElement('img');
|
|
487
|
+
image.src = imageUrl;
|
|
488
|
+
image.style.width = '100px';
|
|
489
|
+
image.style.height = '100px';
|
|
490
|
+
image.style.marginBottom = '16px';
|
|
491
|
+
image.style.marginTop = '16px';
|
|
492
|
+
image.onerror = () => {
|
|
493
|
+
image.style.display = 'none';
|
|
494
|
+
};
|
|
495
|
+
contentContainer.appendChild(image);
|
|
496
|
+
}
|
|
497
|
+
// Title
|
|
498
|
+
const titleElement = document.createElement('h2');
|
|
499
|
+
titleElement.textContent = title;
|
|
500
|
+
titleElement.style.fontSize = '22px';
|
|
501
|
+
titleElement.style.fontWeight = '600';
|
|
502
|
+
titleElement.style.color = this.hexToColor(headerColor);
|
|
503
|
+
titleElement.style.margin = '8px 0';
|
|
504
|
+
contentContainer.appendChild(titleElement);
|
|
505
|
+
// Body
|
|
506
|
+
const bodyElement = document.createElement('p');
|
|
507
|
+
bodyElement.textContent = body;
|
|
508
|
+
bodyElement.style.fontSize = '14px';
|
|
509
|
+
bodyElement.style.color = this.hexToColor(messageColor);
|
|
510
|
+
bodyElement.style.fontWeight = '500';
|
|
511
|
+
bodyElement.style.margin = '8px 0 24px 0';
|
|
512
|
+
contentContainer.appendChild(bodyElement);
|
|
513
|
+
// Button handlers
|
|
514
|
+
const onPrimaryButtonPress = () => {
|
|
515
|
+
if (buttonPropertiesPrimary.webOnClickValue === "redirect_to_web_url" ||
|
|
516
|
+
buttonPropertiesPrimary.webOnClickValue === "deeplink_into_app") {
|
|
517
|
+
this.openWebUrl(buttonPropertiesPrimary.webOnClickAction || '');
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
const onSecondaryButtonPress = () => {
|
|
521
|
+
if (buttonPropertiesSecondary.webOnClickValue === "redirect_to_web_url" ||
|
|
522
|
+
buttonPropertiesSecondary.webOnClickValue === "deeplink_into_app") {
|
|
523
|
+
this.openWebUrl(buttonPropertiesSecondary.webOnClickAction || '');
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
// Create buttons
|
|
527
|
+
const buttonsContainer = document.createElement('div');
|
|
528
|
+
buttonsContainer.style.display = 'flex';
|
|
529
|
+
buttonsContainer.style.justifyContent = 'center';
|
|
530
|
+
buttonsContainer.style.width = '100%';
|
|
531
|
+
buttonsContainer.style.gap = '16px';
|
|
532
|
+
if (secondaryButtonText && secondaryButtonText.trim() !== '') {
|
|
533
|
+
// Primary button
|
|
534
|
+
const primaryButton = document.createElement('button');
|
|
535
|
+
primaryButton.textContent = primaryButtonText;
|
|
536
|
+
primaryButton.style.flex = '1';
|
|
537
|
+
primaryButton.style.maxWidth = '140px';
|
|
538
|
+
primaryButton.style.padding = '12px';
|
|
539
|
+
primaryButton.style.backgroundColor = this.hexToColor(buttonPropertiesPrimary.backgroundColor);
|
|
540
|
+
primaryButton.style.color = this.hexToColor(buttonPropertiesPrimary.textColor);
|
|
541
|
+
primaryButton.style.border = `2px solid ${this.hexToColor(buttonPropertiesPrimary.borderColor)}`;
|
|
542
|
+
primaryButton.style.borderRadius = '8px';
|
|
543
|
+
primaryButton.style.fontSize = '16px';
|
|
544
|
+
primaryButton.style.fontWeight = '600';
|
|
545
|
+
primaryButton.style.cursor = 'pointer';
|
|
546
|
+
primaryButton.onclick = () => {
|
|
547
|
+
document.body.removeChild(modalOverlay);
|
|
548
|
+
onPrimaryButtonPress();
|
|
549
|
+
};
|
|
550
|
+
buttonsContainer.appendChild(primaryButton);
|
|
551
|
+
// Secondary button
|
|
552
|
+
const secondaryButton = document.createElement('button');
|
|
553
|
+
secondaryButton.textContent = secondaryButtonText;
|
|
554
|
+
secondaryButton.style.flex = '1';
|
|
555
|
+
secondaryButton.style.maxWidth = '140px';
|
|
556
|
+
secondaryButton.style.padding = '12px';
|
|
557
|
+
secondaryButton.style.backgroundColor = this.hexToColor(buttonPropertiesSecondary.backgroundColor);
|
|
558
|
+
secondaryButton.style.color = this.hexToColor(buttonPropertiesSecondary.textColor);
|
|
559
|
+
secondaryButton.style.border = `2px solid ${this.hexToColor(buttonPropertiesSecondary.borderColor)}`;
|
|
560
|
+
secondaryButton.style.borderRadius = '8px';
|
|
561
|
+
secondaryButton.style.fontSize = '16px';
|
|
562
|
+
secondaryButton.style.fontWeight = '600';
|
|
563
|
+
secondaryButton.style.cursor = 'pointer';
|
|
564
|
+
secondaryButton.onclick = () => {
|
|
565
|
+
document.body.removeChild(modalOverlay);
|
|
566
|
+
onSecondaryButtonPress();
|
|
567
|
+
};
|
|
568
|
+
buttonsContainer.appendChild(secondaryButton);
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
// Single button
|
|
572
|
+
const primaryButton = document.createElement('button');
|
|
573
|
+
primaryButton.textContent = primaryButtonText;
|
|
574
|
+
primaryButton.style.width = '100%';
|
|
575
|
+
primaryButton.style.padding = '12px';
|
|
576
|
+
primaryButton.style.backgroundColor = this.hexToColor(buttonPropertiesPrimary.backgroundColor);
|
|
577
|
+
primaryButton.style.color = this.hexToColor(buttonPropertiesPrimary.textColor);
|
|
578
|
+
primaryButton.style.border = `2px solid ${this.hexToColor(buttonPropertiesPrimary.borderColor)}`;
|
|
579
|
+
primaryButton.style.borderRadius = '8px';
|
|
580
|
+
primaryButton.style.fontSize = '16px';
|
|
581
|
+
primaryButton.style.fontWeight = '600';
|
|
582
|
+
primaryButton.style.cursor = 'pointer';
|
|
583
|
+
primaryButton.onclick = () => {
|
|
584
|
+
document.body.removeChild(modalOverlay);
|
|
585
|
+
onPrimaryButtonPress();
|
|
586
|
+
};
|
|
587
|
+
buttonsContainer.appendChild(primaryButton);
|
|
588
|
+
}
|
|
589
|
+
// Add all elements to the modal
|
|
590
|
+
modalDialog.appendChild(closeButton);
|
|
591
|
+
modalDialog.appendChild(contentContainer);
|
|
592
|
+
contentContainer.appendChild(buttonsContainer);
|
|
593
|
+
modalOverlay.appendChild(modalDialog);
|
|
594
|
+
// Add to document
|
|
595
|
+
document.body.appendChild(modalOverlay);
|
|
596
|
+
// Auto dismiss if enabled
|
|
597
|
+
if (dismissMessageAutomatically) {
|
|
598
|
+
setTimeout(() => {
|
|
599
|
+
if (document.body.contains(modalOverlay)) {
|
|
600
|
+
document.body.removeChild(modalOverlay);
|
|
601
|
+
}
|
|
602
|
+
}, parseInt(dismissMessageInSeconds) * 1000);
|
|
603
|
+
}
|
|
604
|
+
// Add click event to close when clicking outside
|
|
605
|
+
modalOverlay.addEventListener('click', (event) => {
|
|
606
|
+
if (event.target === modalOverlay) {
|
|
607
|
+
document.body.removeChild(modalOverlay);
|
|
608
|
+
}
|
|
609
|
+
});
|
|
610
|
+
}
|
|
217
611
|
}
|
|
218
612
|
exports.WebSDK = WebSDK;
|