datafast 1.0.1

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.
@@ -0,0 +1,789 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, '__esModule', { value: true });
4
+
5
+ // src/core/utils.ts
6
+ function generateVisitorId() {
7
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
8
+ const r = Math.random() * 16 | 0;
9
+ const v = c === "x" ? r : r & 3 | 8;
10
+ return v.toString(16);
11
+ });
12
+ }
13
+ function generateSessionId() {
14
+ return "sxxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
15
+ const r = Math.random() * 16 | 0;
16
+ const v = c === "x" ? r : r & 3 | 8;
17
+ return v.toString(16);
18
+ });
19
+ }
20
+ function isValidVisitorId(id) {
21
+ if (!id || typeof id !== "string" || id.length !== 36) return false;
22
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id);
23
+ }
24
+ function isValidSessionId(id) {
25
+ if (!id || typeof id !== "string" || id.length !== 37) return false;
26
+ return /^s[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(id);
27
+ }
28
+ function buildScreenHref(platform, screenName) {
29
+ const normalizedName = screenName.replace(/^\/+/, "").trim();
30
+ return `datafast://${platform}/${normalizedName}`;
31
+ }
32
+ function buildUserAgent(sdkVersion, platform, osVersion, deviceModel) {
33
+ const os = platform === "ios" ? "iOS" : platform === "android" ? "Android" : "Web";
34
+ return `DataFast/${sdkVersion} (${os} ${osVersion}; ${deviceModel})`;
35
+ }
36
+ function isValidEventName(name) {
37
+ if (!name || typeof name !== "string") return false;
38
+ if (name.length === 0 || name.length > 64) return false;
39
+ return /^[a-z0-9_-]+$/.test(name.toLowerCase());
40
+ }
41
+ function sanitizeCustomProperties(properties) {
42
+ if (!properties || typeof properties !== "object" || Array.isArray(properties)) {
43
+ return {};
44
+ }
45
+ const sanitized = {};
46
+ const entries = Object.entries(properties);
47
+ if (entries.length > 10) {
48
+ console.warn("[DataFast] Maximum 10 custom properties allowed");
49
+ return null;
50
+ }
51
+ for (const [key, value] of entries) {
52
+ if (key === "eventName") continue;
53
+ if (!key || typeof key !== "string" || key.length > 64) {
54
+ console.warn(`[DataFast] Invalid property name: ${key}`);
55
+ return null;
56
+ }
57
+ if (!/^[a-z0-9_-]+$/i.test(key)) {
58
+ console.warn(`[DataFast] Property name must be alphanumeric with _ or -: ${key}`);
59
+ return null;
60
+ }
61
+ let stringValue = value == null ? "" : String(value);
62
+ if (stringValue.length > 255) {
63
+ stringValue = stringValue.substring(0, 255);
64
+ }
65
+ stringValue = stringValue.replace(/[<>'"&]/g, "").replace(/javascript:/gi, "").replace(/on\w+=/gi, "").trim();
66
+ sanitized[key.toLowerCase()] = stringValue;
67
+ }
68
+ return sanitized;
69
+ }
70
+ function generateEventId() {
71
+ return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
72
+ }
73
+ function isSessionExpired(sessionStartTime) {
74
+ const SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
75
+ return Date.now() - sessionStartTime > SESSION_TTL_MS;
76
+ }
77
+ function createLogger(debug) {
78
+ return {
79
+ log: (...args) => {
80
+ if (debug) console.log("[DataFast]", ...args);
81
+ },
82
+ warn: (...args) => {
83
+ if (debug) console.warn("[DataFast]", ...args);
84
+ },
85
+ error: (...args) => {
86
+ console.error("[DataFast]", ...args);
87
+ }
88
+ };
89
+ }
90
+
91
+ // src/core/queue.ts
92
+ var QUEUE_STORAGE_KEY = "datafast_event_queue";
93
+ var MAX_RETRIES = 3;
94
+ var EventQueue = class {
95
+ constructor(options) {
96
+ this.queue = [];
97
+ this.flushTimer = null;
98
+ this.isFlushing = false;
99
+ this.storage = options.storage;
100
+ this.network = options.network;
101
+ this.apiUrl = options.apiUrl;
102
+ this.userAgent = options.userAgent;
103
+ this.flushInterval = options.flushInterval ?? 3e4;
104
+ this.maxQueueSize = options.maxQueueSize ?? 20;
105
+ this.onFlushError = options.onFlushError;
106
+ }
107
+ /**
108
+ * Initialize the queue - load persisted events from storage
109
+ */
110
+ async init() {
111
+ try {
112
+ const stored = await this.storage.getItem(QUEUE_STORAGE_KEY);
113
+ if (stored) {
114
+ const parsed = JSON.parse(stored);
115
+ if (Array.isArray(parsed)) {
116
+ this.queue = parsed;
117
+ }
118
+ }
119
+ } catch {
120
+ this.queue = [];
121
+ }
122
+ this.startFlushTimer();
123
+ if (this.network?.onConnectionChange) {
124
+ this.network.onConnectionChange((isConnected) => {
125
+ if (isConnected) {
126
+ this.flush();
127
+ }
128
+ });
129
+ }
130
+ }
131
+ /**
132
+ * Add an event to the queue
133
+ */
134
+ async enqueue(payload) {
135
+ const event = {
136
+ id: generateEventId(),
137
+ payload,
138
+ timestamp: Date.now(),
139
+ retries: 0
140
+ };
141
+ this.queue.push(event);
142
+ await this.persistQueue();
143
+ if (this.queue.length >= this.maxQueueSize) {
144
+ this.flush();
145
+ }
146
+ }
147
+ /**
148
+ * Flush all queued events to the server
149
+ */
150
+ async flush() {
151
+ if (this.isFlushing || this.queue.length === 0) {
152
+ return;
153
+ }
154
+ if (this.network) {
155
+ const isConnected = await this.network.isConnected();
156
+ if (!isConnected) {
157
+ return;
158
+ }
159
+ }
160
+ this.isFlushing = true;
161
+ const eventsToProcess = [...this.queue];
162
+ for (const event of eventsToProcess) {
163
+ try {
164
+ await this.sendEvent(event.payload);
165
+ this.queue = this.queue.filter((e) => e.id !== event.id);
166
+ } catch (error) {
167
+ event.retries++;
168
+ if (event.retries >= MAX_RETRIES) {
169
+ this.queue = this.queue.filter((e) => e.id !== event.id);
170
+ if (this.onFlushError) {
171
+ this.onFlushError(error, event);
172
+ }
173
+ }
174
+ }
175
+ }
176
+ await this.persistQueue();
177
+ this.isFlushing = false;
178
+ }
179
+ /**
180
+ * Send a single event to the API
181
+ */
182
+ async sendEvent(payload) {
183
+ const response = await fetch(this.apiUrl, {
184
+ method: "POST",
185
+ headers: {
186
+ "Content-Type": "application/json",
187
+ "User-Agent": this.userAgent
188
+ },
189
+ body: JSON.stringify(payload)
190
+ });
191
+ if (!response.ok) {
192
+ throw new Error(`HTTP ${response.status}`);
193
+ }
194
+ }
195
+ /**
196
+ * Persist queue to storage
197
+ */
198
+ async persistQueue() {
199
+ try {
200
+ await this.storage.setItem(QUEUE_STORAGE_KEY, JSON.stringify(this.queue));
201
+ } catch {
202
+ }
203
+ }
204
+ /**
205
+ * Start the periodic flush timer
206
+ */
207
+ startFlushTimer() {
208
+ if (this.flushTimer) {
209
+ clearInterval(this.flushTimer);
210
+ }
211
+ this.flushTimer = setInterval(() => {
212
+ this.flush();
213
+ }, this.flushInterval);
214
+ }
215
+ /**
216
+ * Stop the flush timer and flush remaining events
217
+ */
218
+ async shutdown() {
219
+ if (this.flushTimer) {
220
+ clearInterval(this.flushTimer);
221
+ this.flushTimer = null;
222
+ }
223
+ await this.flush();
224
+ }
225
+ /**
226
+ * Get current queue size
227
+ */
228
+ get size() {
229
+ return this.queue.length;
230
+ }
231
+ /**
232
+ * Clear all queued events
233
+ */
234
+ async clear() {
235
+ this.queue = [];
236
+ await this.persistQueue();
237
+ }
238
+ };
239
+
240
+ // src/core/client.ts
241
+ var SDK_VERSION = "1.0.0";
242
+ var DEFAULT_API_URL = "https://datafa.st/api/events";
243
+ var STORAGE_KEYS = {
244
+ VISITOR_ID: "datafast_visitor_id",
245
+ SESSION_ID: "datafast_session_id",
246
+ SESSION_START: "datafast_session_start",
247
+ IGNORE_TRACKING: "datafast_ignore"
248
+ };
249
+ var DataFastClient = class {
250
+ constructor() {
251
+ this.state = {
252
+ initialized: false,
253
+ visitorId: null,
254
+ sessionId: null,
255
+ sessionStartTime: null,
256
+ lastScreenName: null,
257
+ config: null,
258
+ deviceInfo: null
259
+ };
260
+ this.queue = null;
261
+ this.logger = createLogger(false);
262
+ }
263
+ /**
264
+ * Initialize the SDK with configuration
265
+ */
266
+ async init(config) {
267
+ if (this.state.initialized) {
268
+ this.logger.warn("SDK already initialized");
269
+ return;
270
+ }
271
+ if (!config.appId) {
272
+ throw new Error("[DataFast] appId is required");
273
+ }
274
+ if (!config.domain) {
275
+ throw new Error("[DataFast] domain is required");
276
+ }
277
+ if (!config.storage) {
278
+ throw new Error("[DataFast] storage adapter is required");
279
+ }
280
+ if (!config.platform) {
281
+ throw new Error("[DataFast] platform is required");
282
+ }
283
+ this.state.config = config;
284
+ this.logger = createLogger(config.debug ?? false);
285
+ const ignoreTracking = await config.storage.getItem(STORAGE_KEYS.IGNORE_TRACKING);
286
+ if (ignoreTracking === "true") {
287
+ this.logger.log("Tracking disabled via user opt-out");
288
+ return;
289
+ }
290
+ await this.initVisitorId();
291
+ await this.initSessionId();
292
+ const apiUrl = config.apiUrl ?? DEFAULT_API_URL;
293
+ const userAgent = buildUserAgent(
294
+ SDK_VERSION,
295
+ config.platform,
296
+ config.appVersion ?? "1.0.0",
297
+ "Unknown"
298
+ );
299
+ this.queue = new EventQueue({
300
+ storage: config.storage,
301
+ network: config.network,
302
+ apiUrl,
303
+ userAgent,
304
+ flushInterval: config.flushInterval,
305
+ maxQueueSize: config.maxQueueSize,
306
+ onFlushError: (error, event) => {
307
+ this.logger.error("Failed to send event:", error, event.payload.type);
308
+ }
309
+ });
310
+ await this.queue.init();
311
+ this.state.initialized = true;
312
+ this.logger.log("SDK initialized", { visitorId: this.state.visitorId, sessionId: this.state.sessionId });
313
+ }
314
+ /**
315
+ * Track a screen view (equivalent to pageview)
316
+ */
317
+ async trackScreen(screenName) {
318
+ if (!this.isReady()) return;
319
+ if (!screenName || typeof screenName !== "string") {
320
+ this.logger.warn("trackScreen requires a screen name");
321
+ return;
322
+ }
323
+ const config = this.state.config;
324
+ const referrer = this.state.lastScreenName ? buildScreenHref(config.platform, this.state.lastScreenName) : null;
325
+ const payload = this.buildBasePayload("pageview", screenName);
326
+ payload.referrer = referrer;
327
+ await this.queue.enqueue(payload);
328
+ this.state.lastScreenName = screenName;
329
+ this.logger.log("Screen tracked:", screenName);
330
+ }
331
+ /**
332
+ * Track a custom event
333
+ */
334
+ async track(eventName, properties) {
335
+ if (!this.isReady()) return;
336
+ if (!isValidEventName(eventName)) {
337
+ this.logger.warn(
338
+ "Invalid event name. Use lowercase alphanumeric with _ or -, max 64 chars:",
339
+ eventName
340
+ );
341
+ return;
342
+ }
343
+ const sanitized = sanitizeCustomProperties(properties ?? {});
344
+ if (sanitized === null) {
345
+ this.logger.warn("Custom properties validation failed");
346
+ return;
347
+ }
348
+ const screenName = this.state.lastScreenName ?? "Unknown";
349
+ const payload = this.buildBasePayload("custom", screenName);
350
+ payload.extraData = {
351
+ eventName: eventName.toLowerCase(),
352
+ ...sanitized
353
+ };
354
+ await this.queue.enqueue(payload);
355
+ this.logger.log("Event tracked:", eventName, sanitized);
356
+ }
357
+ /**
358
+ * Identify a user
359
+ */
360
+ async identify(userId, properties) {
361
+ if (!this.isReady()) return;
362
+ if (!userId || typeof userId !== "string") {
363
+ this.logger.warn("identify requires a user_id");
364
+ return;
365
+ }
366
+ const screenName = this.state.lastScreenName ?? "Unknown";
367
+ const payload = this.buildBasePayload("identify", screenName);
368
+ payload.extraData = {
369
+ user_id: userId,
370
+ name: properties?.name ?? "",
371
+ image: properties?.image ?? "",
372
+ ...properties
373
+ };
374
+ await this.queue.enqueue(payload);
375
+ this.logger.log("User identified:", userId);
376
+ }
377
+ /**
378
+ * Track a payment event
379
+ */
380
+ async trackPayment(data) {
381
+ if (!this.isReady()) return;
382
+ if (!data.email || typeof data.email !== "string") {
383
+ this.logger.warn("trackPayment requires an email");
384
+ return;
385
+ }
386
+ const screenName = this.state.lastScreenName ?? "Unknown";
387
+ const payload = this.buildBasePayload("payment", screenName);
388
+ payload.extraData = {
389
+ email: data.email,
390
+ ...data.amount !== void 0 && { amount: data.amount },
391
+ ...data.currency && { currency: data.currency }
392
+ };
393
+ await this.queue.enqueue(payload);
394
+ this.logger.log("Payment tracked:", data.email);
395
+ }
396
+ /**
397
+ * Track an external link click
398
+ */
399
+ async trackExternalLink(url, text) {
400
+ if (!this.isReady()) return;
401
+ if (!url || typeof url !== "string") {
402
+ this.logger.warn("trackExternalLink requires a URL");
403
+ return;
404
+ }
405
+ const screenName = this.state.lastScreenName ?? "Unknown";
406
+ const payload = this.buildBasePayload("external_link", screenName);
407
+ payload.extraData = {
408
+ url,
409
+ ...text && { text }
410
+ };
411
+ await this.queue.enqueue(payload);
412
+ this.logger.log("External link tracked:", url);
413
+ }
414
+ /**
415
+ * Flush all queued events immediately
416
+ */
417
+ async flush() {
418
+ if (this.queue) {
419
+ await this.queue.flush();
420
+ }
421
+ }
422
+ /**
423
+ * Reset the session (start a new session)
424
+ */
425
+ async resetSession() {
426
+ if (!this.state.config) return;
427
+ const newSessionId = generateSessionId();
428
+ const now = Date.now();
429
+ this.state.sessionId = newSessionId;
430
+ this.state.sessionStartTime = now;
431
+ this.state.lastScreenName = null;
432
+ await this.state.config.storage.setItem(STORAGE_KEYS.SESSION_ID, newSessionId);
433
+ await this.state.config.storage.setItem(STORAGE_KEYS.SESSION_START, now.toString());
434
+ this.logger.log("Session reset:", newSessionId);
435
+ }
436
+ /**
437
+ * Reset the visitor (new anonymous user)
438
+ */
439
+ async reset() {
440
+ if (!this.state.config) return;
441
+ const newVisitorId = generateVisitorId();
442
+ const newSessionId = generateSessionId();
443
+ const now = Date.now();
444
+ this.state.visitorId = newVisitorId;
445
+ this.state.sessionId = newSessionId;
446
+ this.state.sessionStartTime = now;
447
+ this.state.lastScreenName = null;
448
+ await this.state.config.storage.setItem(STORAGE_KEYS.VISITOR_ID, newVisitorId);
449
+ await this.state.config.storage.setItem(STORAGE_KEYS.SESSION_ID, newSessionId);
450
+ await this.state.config.storage.setItem(STORAGE_KEYS.SESSION_START, now.toString());
451
+ if (this.queue) {
452
+ await this.queue.clear();
453
+ }
454
+ this.logger.log("Visitor reset:", newVisitorId);
455
+ }
456
+ /**
457
+ * Opt out of tracking
458
+ */
459
+ async optOut() {
460
+ if (!this.state.config) return;
461
+ await this.state.config.storage.setItem(STORAGE_KEYS.IGNORE_TRACKING, "true");
462
+ this.state.initialized = false;
463
+ if (this.queue) {
464
+ await this.queue.clear();
465
+ await this.queue.shutdown();
466
+ }
467
+ this.logger.log("User opted out of tracking");
468
+ }
469
+ /**
470
+ * Opt back into tracking
471
+ */
472
+ async optIn() {
473
+ if (!this.state.config) return;
474
+ await this.state.config.storage.removeItem(STORAGE_KEYS.IGNORE_TRACKING);
475
+ this.logger.log("User opted back into tracking. Call init() to restart.");
476
+ }
477
+ /**
478
+ * Set device info (call this when device info becomes available)
479
+ */
480
+ setDeviceInfo(info) {
481
+ this.state.deviceInfo = {
482
+ platform: info.platform ?? this.state.config?.platform ?? "ios",
483
+ osVersion: info.osVersion ?? "0.0",
484
+ deviceModel: info.deviceModel ?? "Unknown",
485
+ appVersion: info.appVersion ?? this.state.config?.appVersion ?? "1.0.0",
486
+ screenWidth: info.screenWidth ?? 0,
487
+ screenHeight: info.screenHeight ?? 0,
488
+ viewport: info.viewport ?? { width: 0, height: 0 },
489
+ language: info.language ?? "en",
490
+ timezone: info.timezone ?? "UTC"
491
+ };
492
+ }
493
+ /**
494
+ * Get current visitor ID
495
+ */
496
+ getVisitorId() {
497
+ return this.state.visitorId;
498
+ }
499
+ /**
500
+ * Get current session ID
501
+ */
502
+ getSessionId() {
503
+ return this.state.sessionId;
504
+ }
505
+ /**
506
+ * Check if SDK is initialized and ready
507
+ */
508
+ isInitialized() {
509
+ return this.state.initialized;
510
+ }
511
+ /**
512
+ * Shutdown the SDK
513
+ */
514
+ async shutdown() {
515
+ if (this.queue) {
516
+ await this.queue.shutdown();
517
+ }
518
+ this.state.initialized = false;
519
+ }
520
+ // Private methods
521
+ isReady() {
522
+ if (!this.state.initialized) {
523
+ this.logger.warn("SDK not initialized. Call init() first.");
524
+ return false;
525
+ }
526
+ return true;
527
+ }
528
+ async initVisitorId() {
529
+ const config = this.state.config;
530
+ let visitorId = await config.storage.getItem(STORAGE_KEYS.VISITOR_ID);
531
+ if (!visitorId || !isValidVisitorId(visitorId)) {
532
+ visitorId = generateVisitorId();
533
+ await config.storage.setItem(STORAGE_KEYS.VISITOR_ID, visitorId);
534
+ }
535
+ this.state.visitorId = visitorId;
536
+ }
537
+ async initSessionId() {
538
+ const config = this.state.config;
539
+ let sessionId = await config.storage.getItem(STORAGE_KEYS.SESSION_ID);
540
+ const sessionStartStr = await config.storage.getItem(STORAGE_KEYS.SESSION_START);
541
+ const sessionStart = sessionStartStr ? parseInt(sessionStartStr, 10) : 0;
542
+ if (!sessionId || !isValidSessionId(sessionId) || isSessionExpired(sessionStart)) {
543
+ sessionId = generateSessionId();
544
+ const now = Date.now();
545
+ await config.storage.setItem(STORAGE_KEYS.SESSION_ID, sessionId);
546
+ await config.storage.setItem(STORAGE_KEYS.SESSION_START, now.toString());
547
+ this.state.sessionStartTime = now;
548
+ } else {
549
+ this.state.sessionStartTime = sessionStart;
550
+ }
551
+ this.state.sessionId = sessionId;
552
+ }
553
+ buildBasePayload(type, screenName) {
554
+ const config = this.state.config;
555
+ const deviceInfo = this.state.deviceInfo;
556
+ return {
557
+ websiteId: config.appId,
558
+ domain: config.domain,
559
+ visitorId: this.state.visitorId,
560
+ sessionId: this.state.sessionId,
561
+ href: buildScreenHref(config.platform, screenName),
562
+ referrer: null,
563
+ type,
564
+ viewport: deviceInfo?.viewport ?? { width: 0, height: 0 },
565
+ language: deviceInfo?.language ?? "en",
566
+ timezone: deviceInfo?.timezone ?? "UTC",
567
+ screenWidth: deviceInfo?.screenWidth ?? 0,
568
+ screenHeight: deviceInfo?.screenHeight ?? 0
569
+ };
570
+ }
571
+ };
572
+ var instance = null;
573
+ function getDataFastClient() {
574
+ if (!instance) {
575
+ instance = new DataFastClient();
576
+ }
577
+ return instance;
578
+ }
579
+ function createDataFastClient() {
580
+ return new DataFastClient();
581
+ }
582
+
583
+ // src/web/storage.ts
584
+ function createLocalStorageAdapter() {
585
+ return {
586
+ async getItem(key) {
587
+ try {
588
+ return localStorage.getItem(key);
589
+ } catch (error) {
590
+ console.warn("[DataFast] localStorage.getItem failed:", error);
591
+ return null;
592
+ }
593
+ },
594
+ async setItem(key, value) {
595
+ try {
596
+ localStorage.setItem(key, value);
597
+ } catch (error) {
598
+ console.warn("[DataFast] localStorage.setItem failed:", error);
599
+ }
600
+ },
601
+ async removeItem(key) {
602
+ try {
603
+ localStorage.removeItem(key);
604
+ } catch (error) {
605
+ console.warn("[DataFast] localStorage.removeItem failed:", error);
606
+ }
607
+ }
608
+ };
609
+ }
610
+ function createMemoryStorageAdapter() {
611
+ const storage = /* @__PURE__ */ new Map();
612
+ return {
613
+ async getItem(key) {
614
+ return storage.get(key) ?? null;
615
+ },
616
+ async setItem(key, value) {
617
+ storage.set(key, value);
618
+ },
619
+ async removeItem(key) {
620
+ storage.delete(key);
621
+ }
622
+ };
623
+ }
624
+
625
+ // src/web/network.ts
626
+ function createFetchNetworkAdapter() {
627
+ return {
628
+ async isConnected() {
629
+ return true;
630
+ },
631
+ onConnectionChange(callback) {
632
+ const handleOnline = () => callback(true);
633
+ const handleOffline = () => callback(false);
634
+ window.addEventListener("online", handleOnline);
635
+ window.addEventListener("offline", handleOffline);
636
+ return () => {
637
+ window.removeEventListener("online", handleOnline);
638
+ window.removeEventListener("offline", handleOffline);
639
+ };
640
+ }
641
+ };
642
+ }
643
+
644
+ // src/web/device.ts
645
+ function getDeviceInfo() {
646
+ const getViewport = () => {
647
+ if (typeof window === "undefined") return { width: 0, height: 0 };
648
+ return {
649
+ width: window.innerWidth || 0,
650
+ height: window.innerHeight || 0
651
+ };
652
+ };
653
+ const getScreenSize = () => {
654
+ if (typeof window === "undefined" || !window.screen) return { width: 0, height: 0 };
655
+ return {
656
+ width: window.screen.width || 0,
657
+ height: window.screen.height || 0
658
+ };
659
+ };
660
+ const getLanguage = () => {
661
+ if (typeof navigator === "undefined") return "en";
662
+ return navigator.language || "en";
663
+ };
664
+ const getTimezone = () => {
665
+ try {
666
+ return Intl.DateTimeFormat().resolvedOptions().timeZone || "UTC";
667
+ } catch {
668
+ return "UTC";
669
+ }
670
+ };
671
+ const getUserAgent = () => {
672
+ if (typeof navigator === "undefined") return "Unknown";
673
+ return navigator.userAgent || "Unknown";
674
+ };
675
+ const viewport = getViewport();
676
+ const screen = getScreenSize();
677
+ return {
678
+ platform: "web",
679
+ osVersion: getUserAgent(),
680
+ deviceModel: "Browser",
681
+ appVersion: "1.0.0",
682
+ screenWidth: screen.width,
683
+ screenHeight: screen.height,
684
+ viewport,
685
+ language: getLanguage(),
686
+ timezone: getTimezone()
687
+ };
688
+ }
689
+ function onViewportChange(callback) {
690
+ if (typeof window === "undefined") {
691
+ return () => {
692
+ };
693
+ }
694
+ const handleResize = () => {
695
+ callback({
696
+ width: window.innerWidth || 0,
697
+ height: window.innerHeight || 0
698
+ });
699
+ };
700
+ window.addEventListener("resize", handleResize);
701
+ return () => {
702
+ window.removeEventListener("resize", handleResize);
703
+ };
704
+ }
705
+
706
+ // src/web/index.ts
707
+ async function initDataFast(config) {
708
+ const client = getDataFastClient();
709
+ let storage;
710
+ try {
711
+ localStorage.setItem("__datafast_test__", "test");
712
+ localStorage.removeItem("__datafast_test__");
713
+ storage = createLocalStorageAdapter();
714
+ } catch {
715
+ console.warn("[DataFast] localStorage not available, using in-memory storage");
716
+ storage = createMemoryStorageAdapter();
717
+ }
718
+ const network = createFetchNetworkAdapter();
719
+ const deviceInfo = getDeviceInfo();
720
+ const domain = config.domain ?? (typeof window !== "undefined" ? window.location.hostname : "unknown");
721
+ await client.init({
722
+ appId: config.websiteId,
723
+ domain,
724
+ storage,
725
+ network,
726
+ platform: "web",
727
+ apiUrl: config.apiUrl,
728
+ debug: config.debug,
729
+ flushInterval: config.flushInterval ?? 5e3,
730
+ maxQueueSize: config.maxQueueSize ?? 10
731
+ });
732
+ client.setDeviceInfo(deviceInfo);
733
+ onViewportChange((viewport) => {
734
+ client.setDeviceInfo({
735
+ ...deviceInfo,
736
+ viewport
737
+ });
738
+ });
739
+ const webClient = client;
740
+ webClient.trackPageview = async (path) => {
741
+ const pagePath = path ?? (typeof window !== "undefined" ? window.location.pathname : "/");
742
+ await client.trackScreen(pagePath);
743
+ };
744
+ return webClient;
745
+ }
746
+ async function createDataFastWithAdapters(config) {
747
+ const client = createDataFastClient();
748
+ await client.init(config);
749
+ const deviceInfo = getDeviceInfo();
750
+ client.setDeviceInfo(deviceInfo);
751
+ return client;
752
+ }
753
+ async function createDataFastWeb(config) {
754
+ return initDataFast(config);
755
+ }
756
+ var dataFastWeb = {
757
+ init: initDataFast,
758
+ createWeb: createDataFastWeb,
759
+ getClient: getDataFastClient,
760
+ createClient: createDataFastClient
761
+ };
762
+ var web_default = dataFastWeb;
763
+
764
+ exports.DataFastClient = DataFastClient;
765
+ exports.EventQueue = EventQueue;
766
+ exports.buildScreenHref = buildScreenHref;
767
+ exports.buildUserAgent = buildUserAgent;
768
+ exports.createDataFastClient = createDataFastClient;
769
+ exports.createDataFastWeb = createDataFastWeb;
770
+ exports.createDataFastWithAdapters = createDataFastWithAdapters;
771
+ exports.createFetchNetworkAdapter = createFetchNetworkAdapter;
772
+ exports.createLocalStorageAdapter = createLocalStorageAdapter;
773
+ exports.createLogger = createLogger;
774
+ exports.createMemoryStorageAdapter = createMemoryStorageAdapter;
775
+ exports.default = web_default;
776
+ exports.generateEventId = generateEventId;
777
+ exports.generateSessionId = generateSessionId;
778
+ exports.generateVisitorId = generateVisitorId;
779
+ exports.getDataFastClient = getDataFastClient;
780
+ exports.getDeviceInfo = getDeviceInfo;
781
+ exports.initDataFast = initDataFast;
782
+ exports.isSessionExpired = isSessionExpired;
783
+ exports.isValidEventName = isValidEventName;
784
+ exports.isValidSessionId = isValidSessionId;
785
+ exports.isValidVisitorId = isValidVisitorId;
786
+ exports.onViewportChange = onViewportChange;
787
+ exports.sanitizeCustomProperties = sanitizeCustomProperties;
788
+ //# sourceMappingURL=index.js.map
789
+ //# sourceMappingURL=index.js.map