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