@bytem/bytem-tracker-app 0.0.3 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +50 -25
  2. package/dist/src/BytemTracker.d.ts +58 -0
  3. package/dist/src/BytemTracker.js +474 -0
  4. package/dist/src/business/index.d.ts +4 -0
  5. package/dist/src/business/index.js +20 -0
  6. package/dist/src/business/trackCheckOutOrder.d.ts +2 -0
  7. package/dist/src/business/trackCheckOutOrder.js +20 -0
  8. package/dist/src/business/trackPayOrder.d.ts +2 -0
  9. package/dist/src/business/trackPayOrder.js +21 -0
  10. package/dist/src/business/trackUser.d.ts +2 -0
  11. package/dist/src/business/trackUser.js +13 -0
  12. package/dist/src/business/trackViewProduct.d.ts +2 -0
  13. package/dist/src/business/trackViewProduct.js +16 -0
  14. package/dist/src/core/base.d.ts +2 -0
  15. package/dist/src/core/base.js +24 -0
  16. package/dist/src/core/device.d.ts +3 -0
  17. package/dist/src/core/device.js +45 -0
  18. package/dist/src/core/request.d.ts +2 -0
  19. package/dist/src/core/request.js +21 -0
  20. package/dist/src/core/session.d.ts +17 -0
  21. package/dist/src/core/session.js +59 -0
  22. package/dist/src/core/storage.d.ts +9 -0
  23. package/dist/src/core/storage.js +41 -0
  24. package/dist/src/index.d.ts +3 -0
  25. package/dist/src/index.js +22 -0
  26. package/dist/src/types.d.ts +67 -0
  27. package/dist/src/types.js +12 -0
  28. package/dist/test/BytemTracker.test.d.ts +1 -0
  29. package/dist/test/BytemTracker.test.js +248 -0
  30. package/dist/test/debug.test.d.ts +1 -0
  31. package/dist/test/debug.test.js +90 -0
  32. package/dist/test/setup.d.ts +1 -0
  33. package/dist/test/setup.js +57 -0
  34. package/package.json +30 -11
  35. package/dist/env.d.ts +0 -1
  36. package/dist/env.js +0 -6
  37. package/dist/index.d.ts +0 -4
  38. package/dist/index.js +0 -6
  39. package/dist/tracker.d.ts +0 -26
  40. package/dist/tracker.js +0 -120
  41. package/dist/types.d.ts +0 -24
  42. package/dist/types.js +0 -2
  43. package/dist/uuid.d.ts +0 -1
  44. package/dist/uuid.js +0 -19
package/README.md CHANGED
@@ -1,41 +1,66 @@
1
- # @bytem/bytem-tracker
1
+ # Bytem Tracker SDK (React Native)
2
2
 
3
- ByteM 埋点 SDKReact Native / Expo)
3
+ This is the official Bytem Tracker SDK for React Native applications.
4
4
 
5
- > 本 SDK 为 Flutter `bytem_tracker.dart` 的 **1:1 行为迁移版本**
6
- > 字段、默认值、payload 结构完全一致,后端无需任何改动
5
+ ## Documentation
6
+
7
+ - [Usage Guide](docs/USAGE.md): Detailed instructions on initialization, event tracking, and API usage.
8
+
9
+ ## Installation
10
+
11
+ Install the package and its peer dependencies using Yarn:
7
12
 
8
- ### 此项目运行起来
9
13
  ```bash
10
- rm -rf dist
11
- yarn install
12
- yarn build
14
+ yarn add @bytem/bytem-tracker-app @react-native-async-storage/async-storage react-native-device-info uuid
13
15
  ```
14
16
 
15
- ---
17
+ ### iOS Setup
18
+ Don't forget to install pods for iOS:
19
+ ```bash
20
+ cd ios && pod install
21
+ ```
22
+
23
+ ## Development
24
+
25
+ This project uses **Yarn** for dependency management.
16
26
 
17
- ## ✨ 特性
27
+ ### Setup
18
28
 
19
- - ✅ React Native / Expo 通用
20
- - ✅ 行为 100% 对齐 Flutter SDK
21
- - ✅ 单例模式
22
- - ✅ 自动 deviceId / visitorId 管理
23
- - ✅ TypeScript 支持
24
- - ❌ 不支持 Web(刻意限制,避免误用)
29
+ ```bash
30
+ yarn install
31
+ ```
25
32
 
26
- ---
33
+ ### Running Tests
27
34
 
28
- ## 📦 安装
35
+ ```bash
36
+ yarn test
37
+ ```
29
38
 
30
- ### React Native CLI
39
+ ### Build
31
40
 
32
41
  ```bash
33
- yarn add @bytem/bytem-tracker
34
- yarn add @react-native-async-storage/async-storage
42
+ yarn build
35
43
  ```
36
44
 
37
- ### Expo
45
+ ## Release & Publish
46
+
47
+ Automate version bump and publish via npm scripts:
48
+
38
49
  ```bash
39
- expo install @react-native-async-storage/async-storage
40
- yarn add @bytem/bytem-tracker
41
- ```
50
+ # Patch release (1.0.0 -> 1.0.1)
51
+ npm run release:patch
52
+
53
+ # Minor release (1.0.0 -> 1.1.0)
54
+ npm run release:minor
55
+
56
+ # Major release (1.0.0 -> 2.0.0)
57
+ npm run release:major
58
+
59
+ # Beta prerelease (1.0.0 -> 1.0.1-beta.0)
60
+ npm run release:beta
61
+ ```
62
+
63
+ Notes:
64
+ - These scripts bump package.json version and publish to npm.
65
+ - `prepublishOnly` builds the package before publishing.
66
+ - Configure auth via user-level `~/.npmrc`. Do not commit tokens in the repo.
@@ -0,0 +1,58 @@
1
+ import { TrackerConfig, Product, Order, UserTraits } from './types';
2
+ declare class BytemTracker {
3
+ private static instance;
4
+ private readonly SDK_NAME;
5
+ private readonly SDK_VERSION;
6
+ private readonly DEFAULT_ENDPOINT;
7
+ private readonly DEFAULT_API_PATH;
8
+ private appKey;
9
+ private baseUrl;
10
+ private debug;
11
+ private visitorId;
12
+ private deviceIdType;
13
+ private appScheme;
14
+ private apiPath;
15
+ private isInitialized;
16
+ private sessionStarted;
17
+ private useSessionCookie;
18
+ private sessionCookieTimeout;
19
+ private lastBeat;
20
+ private trackTime;
21
+ private storedDuration;
22
+ private lastViewTime;
23
+ private lastViewStoredDuration;
24
+ private constructor();
25
+ static getInstance(): BytemTracker;
26
+ init(config: TrackerConfig): Promise<void>;
27
+ private ensureInitialized;
28
+ beginSession(force?: boolean): Promise<void>;
29
+ sessionDuration(sec: number): Promise<void>;
30
+ endSession(sec?: number, force?: boolean): Promise<void>;
31
+ trackSessions(): Promise<void>;
32
+ startTime(): void;
33
+ stopTime(): void;
34
+ trackEvent(eventKey: string, segmentation?: Record<string, any>, count?: number, sum?: number, duration?: number): Promise<void>;
35
+ trackPageview(current: string, referrer?: string): Promise<void>;
36
+ trackGoodsClick(params: {
37
+ gid?: string;
38
+ skuid?: string;
39
+ url: string;
40
+ source?: string;
41
+ position?: string;
42
+ current?: string;
43
+ referrer?: string;
44
+ }): Promise<void>;
45
+ private reportViewDuration;
46
+ private getRealDeviceId;
47
+ private getMetrics;
48
+ private buildBaseRequestParams;
49
+ private extendSession;
50
+ private sendRequest;
51
+ trackViewProduct(product: Product): Promise<void>;
52
+ trackCheckOutOrder(order: Order): Promise<void>;
53
+ trackPayOrder(order: Order): Promise<void>;
54
+ trackUser(userId: string, traits?: UserTraits): Promise<void>;
55
+ private sendUserDetails;
56
+ }
57
+ declare const _default: BytemTracker;
58
+ export default _default;
@@ -0,0 +1,474 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const types_1 = require("./types");
4
+ const storage_1 = require("./core/storage");
5
+ const device_1 = require("./core/device");
6
+ class BytemTracker {
7
+ constructor() {
8
+ // SDK Constants
9
+ this.SDK_NAME = 'react_native_bytem';
10
+ this.SDK_VERSION = '0.0.1'; // Should match package.json
11
+ this.DEFAULT_ENDPOINT = 'https://tracking.server.bytecon.com';
12
+ this.DEFAULT_API_PATH = '/i';
13
+ // Config
14
+ this.appKey = null;
15
+ this.baseUrl = this.DEFAULT_ENDPOINT;
16
+ this.debug = false;
17
+ this.visitorId = null;
18
+ this.deviceIdType = 1; // 1: Auto-generated
19
+ this.appScheme = null;
20
+ this.apiPath = this.DEFAULT_API_PATH;
21
+ this.isInitialized = false;
22
+ // Session Management
23
+ this.sessionStarted = false;
24
+ this.useSessionCookie = true;
25
+ this.sessionCookieTimeout = 30; // minutes
26
+ this.lastBeat = null;
27
+ this.trackTime = true;
28
+ this.storedDuration = 0;
29
+ this.lastViewTime = 0;
30
+ this.lastViewStoredDuration = 0;
31
+ }
32
+ static getInstance() {
33
+ if (!BytemTracker.instance) {
34
+ BytemTracker.instance = new BytemTracker();
35
+ }
36
+ return BytemTracker.instance;
37
+ }
38
+ async init(config) {
39
+ if (this.isInitialized) {
40
+ console.warn('[BytemTracker] Already initialized');
41
+ return;
42
+ }
43
+ this.appKey = config.appId;
44
+ if (config.endpoint) {
45
+ this.baseUrl = config.endpoint;
46
+ }
47
+ this.debug = !!config.debug;
48
+ this.appScheme = config.appScheme || null;
49
+ if (config.path) {
50
+ this.apiPath = config.path.startsWith('/') ? config.path : '/' + config.path;
51
+ }
52
+ // Initialize Visitor ID
53
+ if (config.visitorId) {
54
+ this.visitorId = config.visitorId;
55
+ await (0, storage_1.setItem)(storage_1.StorageKeys.VISITOR_ID, this.visitorId);
56
+ if (this.debug)
57
+ console.log(`[BytemTracker] Using provided visitor ID: ${this.visitorId}`);
58
+ }
59
+ else {
60
+ this.visitorId = await (0, storage_1.getItem)(storage_1.StorageKeys.VISITOR_ID);
61
+ if (!this.visitorId) {
62
+ this.visitorId = (0, device_1.generateUUID)();
63
+ await (0, storage_1.setItem)(storage_1.StorageKeys.VISITOR_ID, this.visitorId);
64
+ if (this.debug)
65
+ console.log(`[BytemTracker] Generated new visitor ID: ${this.visitorId}`);
66
+ }
67
+ else {
68
+ if (this.debug)
69
+ console.log(`[BytemTracker] Restored visitor ID: ${this.visitorId}`);
70
+ }
71
+ }
72
+ // Initialize Device ID type
73
+ if (config.deviceId) {
74
+ this.deviceIdType = 0; // Developer set
75
+ await (0, storage_1.setItem)(storage_1.StorageKeys.DEVICE_ID, config.deviceId);
76
+ if (this.debug)
77
+ console.log(`[BytemTracker] Using developer-set device ID: ${config.deviceId}`);
78
+ }
79
+ else {
80
+ this.deviceIdType = 1; // Auto generated (using visitor ID or platform ID logic)
81
+ }
82
+ this.isInitialized = true;
83
+ if (this.debug) {
84
+ console.log('[BytemTracker] Initialized ✅');
85
+ console.log(` app_key: ${this.appKey}`);
86
+ console.log(` base_url: ${this.baseUrl}`);
87
+ console.log(` visitor_id: ${this.visitorId}`);
88
+ if (this.appScheme)
89
+ console.log(` app_scheme: ${this.appScheme}`);
90
+ }
91
+ // Begin session
92
+ try {
93
+ await this.beginSession();
94
+ }
95
+ catch (e) {
96
+ if (this.debug) {
97
+ console.warn('[BytemTracker] Failed to begin session during init:', e);
98
+ }
99
+ this.sessionStarted = false;
100
+ }
101
+ }
102
+ ensureInitialized() {
103
+ if (!this.isInitialized) {
104
+ throw new Error('[BytemTracker] Not initialized. Call await init() first.');
105
+ }
106
+ }
107
+ async beginSession(force = false) {
108
+ this.ensureInitialized();
109
+ if (this.sessionStarted) {
110
+ if (this.debug)
111
+ console.log('[BytemTracker] Session already started, skipping');
112
+ return;
113
+ }
114
+ try {
115
+ this.sessionStarted = true;
116
+ this.lastBeat = Date.now();
117
+ // Check session cookie
118
+ const sessionKey = `${this.appKey}/cly_session`;
119
+ const expireTimestampStr = await (0, storage_1.getItem)(sessionKey);
120
+ const expireTimestamp = expireTimestampStr ? parseInt(expireTimestampStr, 10) : null;
121
+ const currentTimestamp = Date.now();
122
+ const shouldSendRequest = force ||
123
+ !this.useSessionCookie ||
124
+ expireTimestamp === null ||
125
+ expireTimestamp <= currentTimestamp;
126
+ if (!shouldSendRequest) {
127
+ if (this.debug)
128
+ console.log('[BytemTracker] Session cookie still valid, skipping begin_session request');
129
+ // Update cookie
130
+ await (0, storage_1.setItem)(sessionKey, (currentTimestamp + this.sessionCookieTimeout * 60 * 1000).toString());
131
+ return;
132
+ }
133
+ const deviceId = await this.getRealDeviceId();
134
+ const metrics = await this.getMetrics();
135
+ const body = await this.buildBaseRequestParams(deviceId);
136
+ body['begin_session'] = '1';
137
+ body['metrics'] = JSON.stringify(metrics);
138
+ await this.sendRequest(this.apiPath, body, 'Begin Session');
139
+ await (0, storage_1.setItem)(sessionKey, (currentTimestamp + this.sessionCookieTimeout * 60 * 1000).toString());
140
+ }
141
+ catch (e) {
142
+ this.sessionStarted = false;
143
+ if (this.debug)
144
+ console.error('[BytemTracker] Error in beginSession:', e);
145
+ }
146
+ }
147
+ async sessionDuration(sec) {
148
+ this.ensureInitialized();
149
+ if (!this.sessionStarted) {
150
+ if (this.debug)
151
+ console.log('[BytemTracker] Session not started, skipping session_duration');
152
+ return;
153
+ }
154
+ const deviceId = await this.getRealDeviceId();
155
+ const body = await this.buildBaseRequestParams(deviceId);
156
+ body['session_duration'] = sec.toString();
157
+ await this.sendRequest(this.apiPath, body, 'Session Duration');
158
+ this.lastBeat = Date.now();
159
+ await this.extendSession();
160
+ }
161
+ async endSession(sec, force = false) {
162
+ this.ensureInitialized();
163
+ if (!this.sessionStarted) {
164
+ if (this.debug)
165
+ console.log('[BytemTracker] Session not started, skipping end_session');
166
+ return;
167
+ }
168
+ const currentTimestamp = Date.now();
169
+ const sessionSec = sec !== null && sec !== void 0 ? sec : (this.lastBeat ? (currentTimestamp - this.lastBeat) / 1000 : 0);
170
+ if (this.debug)
171
+ console.log(`[BytemTracker] Ending session with duration: ${sessionSec} seconds`);
172
+ if (this.useSessionCookie && !force) {
173
+ await this.sessionDuration(sessionSec);
174
+ this.sessionStarted = false;
175
+ return;
176
+ }
177
+ const deviceId = await this.getRealDeviceId();
178
+ const body = await this.buildBaseRequestParams(deviceId);
179
+ body['end_session'] = '1';
180
+ body['session_duration'] = sessionSec.toString();
181
+ await this.sendRequest(this.apiPath, body, 'End Session');
182
+ this.sessionStarted = false;
183
+ }
184
+ async trackSessions() {
185
+ this.ensureInitialized();
186
+ if (this.debug)
187
+ console.log('[BytemTracker] trackSessions called');
188
+ if (this.sessionStarted && this.lastBeat) {
189
+ const duration = (Date.now() - this.lastBeat) / 1000;
190
+ if (duration > 0) {
191
+ if (this.debug)
192
+ console.log(`[BytemTracker] Session already started, sending session_duration: ${duration}s`);
193
+ await this.sessionDuration(duration);
194
+ }
195
+ }
196
+ else {
197
+ await this.beginSession();
198
+ }
199
+ this.startTime();
200
+ if (this.debug)
201
+ console.log('[BytemTracker] Session tracking started');
202
+ }
203
+ startTime() {
204
+ if (!this.trackTime) {
205
+ this.trackTime = true;
206
+ const now = Date.now();
207
+ if (this.lastBeat && this.storedDuration > 0) {
208
+ this.lastBeat = now - this.storedDuration;
209
+ this.storedDuration = 0;
210
+ }
211
+ else {
212
+ this.lastBeat = now;
213
+ }
214
+ if (this.lastViewStoredDuration > 0 && this.lastViewTime > 0) {
215
+ this.lastViewTime = now - this.lastViewStoredDuration;
216
+ this.lastViewStoredDuration = 0;
217
+ }
218
+ else {
219
+ this.lastViewTime = now;
220
+ }
221
+ if (this.debug)
222
+ console.log('[BytemTracker] Time tracking started');
223
+ this.extendSession();
224
+ }
225
+ }
226
+ stopTime() {
227
+ if (this.trackTime) {
228
+ this.trackTime = false;
229
+ const now = Date.now();
230
+ if (this.lastBeat) {
231
+ this.storedDuration = now - this.lastBeat;
232
+ }
233
+ if (this.lastViewTime > 0) {
234
+ this.lastViewStoredDuration = now - this.lastViewTime;
235
+ }
236
+ if (this.debug)
237
+ console.log('[BytemTracker] Time tracking stopped');
238
+ }
239
+ }
240
+ async trackEvent(eventKey, segmentation, count = 1, sum, duration) {
241
+ this.ensureInitialized();
242
+ const deviceId = await this.getRealDeviceId();
243
+ const now = new Date();
244
+ const event = {
245
+ key: eventKey,
246
+ count: count,
247
+ segmentation: segmentation,
248
+ timestamp: now.getTime(),
249
+ hour: now.getHours(),
250
+ dow: now.getDay() === 0 ? 0 : now.getDay(), // JS getDay: 0=Sun, Dart: 7=Sun but mapped to 0. 0-6.
251
+ };
252
+ if (sum !== undefined)
253
+ event.sum = sum;
254
+ if (duration !== undefined)
255
+ event.dur = duration;
256
+ const body = await this.buildBaseRequestParams(deviceId);
257
+ body['events'] = JSON.stringify([event]);
258
+ await this.sendRequest(this.apiPath, body, 'Event');
259
+ }
260
+ async trackPageview(current, referrer) {
261
+ await this.reportViewDuration();
262
+ const segmentation = {
263
+ name: current,
264
+ current: current,
265
+ referrer: '',
266
+ };
267
+ if (referrer)
268
+ segmentation.referrer = referrer;
269
+ let duration;
270
+ if (this.lastViewTime > 0) {
271
+ const now = Date.now();
272
+ duration = this.trackTime
273
+ ? (now - this.lastViewTime) / 1000
274
+ : this.lastViewStoredDuration / 1000;
275
+ }
276
+ this.lastViewTime = Date.now();
277
+ if (!this.trackTime) {
278
+ this.lastViewStoredDuration = 0;
279
+ }
280
+ await this.trackEvent(types_1.BytemEventKeys.view, segmentation, 1, undefined, duration);
281
+ }
282
+ async trackGoodsClick(params) {
283
+ this.ensureInitialized();
284
+ const segmentation = {
285
+ gid: params.gid || '',
286
+ skuid: params.skuid || '',
287
+ url: params.url,
288
+ current: params.current || '',
289
+ referrer: params.referrer || '',
290
+ visitor_id: this.visitorId || '',
291
+ };
292
+ if (this.appScheme)
293
+ segmentation.domain = this.appScheme;
294
+ if (params.source)
295
+ segmentation.source = params.source;
296
+ if (params.position)
297
+ segmentation.position = params.position;
298
+ await this.trackEvent('goods_click', segmentation); // Using string literal as Dart does in implementation
299
+ }
300
+ // --- Helpers ---
301
+ async reportViewDuration() {
302
+ if (this.lastViewTime === 0)
303
+ return;
304
+ const now = Date.now();
305
+ const duration = this.trackTime
306
+ ? (now - this.lastViewTime) / 1000
307
+ : this.lastViewStoredDuration / 1000;
308
+ if (duration > 0) {
309
+ if (this.debug)
310
+ console.log(`[BytemTracker] Last page duration: ${duration}s`);
311
+ }
312
+ }
313
+ async getRealDeviceId() {
314
+ // 1. Check developer set ID
315
+ if (this.deviceIdType === 0) {
316
+ const stored = await (0, storage_1.getItem)(storage_1.StorageKeys.DEVICE_ID);
317
+ if (stored)
318
+ return stored;
319
+ }
320
+ // 2. Use visitor ID
321
+ return this.visitorId || (0, device_1.generateUUID)();
322
+ }
323
+ async getMetrics() {
324
+ const metrics = {
325
+ _sdk_name: this.SDK_NAME,
326
+ _sdk_version: this.SDK_VERSION,
327
+ };
328
+ try {
329
+ const info = await (0, device_1.getDeviceInfo)();
330
+ metrics._platform_version = info.osVersion;
331
+ metrics._app_version = this.SDK_VERSION; // Or get actual app version if available
332
+ metrics._resolution = `${Math.round(info.screenWidth)}x${Math.round(info.screenHeight)}`;
333
+ metrics._locale = info.language;
334
+ // _density could be added if available in getDeviceInfo
335
+ }
336
+ catch (e) {
337
+ if (this.debug)
338
+ console.warn('[BytemTracker] Error getting metrics:', e);
339
+ }
340
+ return metrics;
341
+ }
342
+ async buildBaseRequestParams(deviceId) {
343
+ const now = new Date();
344
+ const visitorId = this.visitorId || (0, device_1.generateUUID)();
345
+ const params = {
346
+ app_key: this.appKey,
347
+ device_id: deviceId,
348
+ kid: visitorId,
349
+ visitor_id: visitorId,
350
+ sdk_name: this.SDK_NAME,
351
+ sdk_version: this.SDK_VERSION,
352
+ t: this.deviceIdType.toString(),
353
+ timestamp: now.getTime().toString(),
354
+ hour: now.getHours().toString(),
355
+ dow: (now.getDay() === 0 ? 0 : now.getDay()).toString(),
356
+ };
357
+ // Add location info if available (not implemented yet in this class properties)
358
+ return params;
359
+ }
360
+ async extendSession() {
361
+ if (!this.useSessionCookie)
362
+ return;
363
+ const sessionKey = `${this.appKey}/cly_session`;
364
+ const expireTimestampStr = await (0, storage_1.getItem)(sessionKey);
365
+ const currentTimestamp = Date.now();
366
+ const expireTimestamp = expireTimestampStr ? parseInt(expireTimestampStr, 10) : null;
367
+ if (expireTimestamp === null || expireTimestamp <= currentTimestamp) {
368
+ if (this.debug)
369
+ console.log('[BytemTracker] Session expired, restarting session');
370
+ this.sessionStarted = false;
371
+ await this.beginSession(true);
372
+ }
373
+ else {
374
+ await (0, storage_1.setItem)(sessionKey, (currentTimestamp + this.sessionCookieTimeout * 60 * 1000).toString());
375
+ }
376
+ }
377
+ async sendRequest(path, body, requestName) {
378
+ // Manually constructing URL and using fetch with urlencoded body
379
+ let requestUrl = this.baseUrl;
380
+ try {
381
+ // Handle endpoint/path logic
382
+ const urlObj = new URL(this.baseUrl);
383
+ if (path) {
384
+ if (path.startsWith('http')) {
385
+ requestUrl = path;
386
+ }
387
+ else {
388
+ // If base url has path, append or replace?
389
+ // Dart implementation just concatenates: "$_baseUrl$path"
390
+ // But _baseUrl usually has no trailing slash, and path starts with /
391
+ const base = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
392
+ const endpointPath = path.startsWith('/') ? path : '/' + path;
393
+ requestUrl = `${base}${endpointPath}`;
394
+ }
395
+ }
396
+ }
397
+ catch (e) {
398
+ requestUrl = this.baseUrl + path;
399
+ }
400
+ if (this.debug) {
401
+ console.log(`📤 [BytemTracker] Sending ${requestName}:`);
402
+ console.log(` URL: ${requestUrl}`);
403
+ console.log(` Body:`, body);
404
+ }
405
+ try {
406
+ // Convert body to urlencoded string
407
+ const formBody = Object.keys(body).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(body[key])).join('&');
408
+ const response = await fetch(requestUrl, {
409
+ method: 'POST',
410
+ headers: {
411
+ 'Content-Type': 'application/x-www-form-urlencoded',
412
+ },
413
+ body: formBody,
414
+ });
415
+ if (this.debug) {
416
+ const resText = await response.text();
417
+ console.log(`📥 [BytemTracker] Response (${response.status}): ${resText}`);
418
+ }
419
+ }
420
+ catch (e) {
421
+ if (this.debug)
422
+ console.error(`❌ [BytemTracker] Error sending ${requestName}:`, e);
423
+ }
424
+ }
425
+ // Wrappers for Business methods to maintain compatibility or you can refactor them too
426
+ async trackViewProduct(product) {
427
+ // Map Product to segmentation
428
+ const segmentation = {
429
+ product_id: product.productId,
430
+ name: product.name,
431
+ price: product.price,
432
+ currency: product.currency,
433
+ category: product.category,
434
+ };
435
+ // In Dart, trackViewProduct sends "view_product" event.
436
+ await this.trackEvent('view_product', segmentation);
437
+ }
438
+ async trackCheckOutOrder(order) {
439
+ // Map Order to segmentation
440
+ const segmentation = {
441
+ order_id: order.orderId,
442
+ total: order.total,
443
+ currency: order.currency,
444
+ // products need to be handled. Dart sends lists of gids, etc.
445
+ // This is a simplification. Ideally we replicate trackCheckOutOrder logic fully.
446
+ };
447
+ await this.trackEvent('check_out_order', segmentation);
448
+ }
449
+ async trackPayOrder(order) {
450
+ const segmentation = {
451
+ order_id: order.orderId,
452
+ total: order.total,
453
+ currency: order.currency,
454
+ };
455
+ await this.trackEvent('pay_order', segmentation);
456
+ }
457
+ async trackUser(userId, traits) {
458
+ // Map to user_details
459
+ // This is a special request in Dart: "user_details" param
460
+ await this.sendUserDetails(userId, traits);
461
+ }
462
+ async sendUserDetails(userId, traits) {
463
+ this.ensureInitialized();
464
+ const deviceId = await this.getRealDeviceId();
465
+ const userData = Object.assign({}, traits);
466
+ if (userId) {
467
+ userData.custom = Object.assign(Object.assign({}, userData.custom), { user_id: userId, visitor_id: this.visitorId });
468
+ }
469
+ const body = await this.buildBaseRequestParams(deviceId);
470
+ body['user_details'] = JSON.stringify(userData);
471
+ await this.sendRequest(this.apiPath, body, 'User Details');
472
+ }
473
+ }
474
+ exports.default = BytemTracker.getInstance();
@@ -0,0 +1,4 @@
1
+ export * from './trackViewProduct';
2
+ export * from './trackCheckOutOrder';
3
+ export * from './trackPayOrder';
4
+ export * from './trackUser';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./trackViewProduct"), exports);
18
+ __exportStar(require("./trackCheckOutOrder"), exports);
19
+ __exportStar(require("./trackPayOrder"), exports);
20
+ __exportStar(require("./trackUser"), exports);
@@ -0,0 +1,2 @@
1
+ import { EventPayload, Order } from '../types';
2
+ export declare const trackCheckOutOrder: (order: Order) => EventPayload;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.trackCheckOutOrder = void 0;
4
+ const trackCheckOutOrder = (order) => {
5
+ return {
6
+ event: 'checkout_order',
7
+ params: {
8
+ order_id: order.orderId,
9
+ total: order.total,
10
+ currency: order.currency,
11
+ products: order.products.map((p) => ({
12
+ product_id: p.productId,
13
+ name: p.name,
14
+ price: p.price,
15
+ quantity: p.quantity,
16
+ })),
17
+ },
18
+ };
19
+ };
20
+ exports.trackCheckOutOrder = trackCheckOutOrder;
@@ -0,0 +1,2 @@
1
+ import { EventPayload, Order } from '../types';
2
+ export declare const trackPayOrder: (order: Order) => EventPayload;