@philcrp/analytics 1.6.4 → 1.6.5

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/index.js ADDED
@@ -0,0 +1,983 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ AnalyticsManager: () => AnalyticsManager,
24
+ BaseAnalytics: () => BaseAnalytics,
25
+ GoogleAnalyticsProvider: () => GoogleAnalyticsProvider,
26
+ PostHogAnalyticsProvider: () => PostHogAnalyticsProvider,
27
+ XAdsAnalyticsProvider: () => XAdsAnalyticsProvider,
28
+ clearGlobalAnalytics: () => clearGlobalAnalytics,
29
+ createAnalytics: () => createAnalytics,
30
+ createSingletonAnalytics: () => createSingletonAnalytics,
31
+ default: () => AnalyticsManager,
32
+ getGlobalAnalytics: () => getGlobalAnalytics,
33
+ getGlobalAnalyticsNames: () => getGlobalAnalyticsNames,
34
+ getGlobalAnalyticsOptional: () => getGlobalAnalyticsOptional,
35
+ getSingletonAnalytics: () => getSingletonAnalytics,
36
+ getSingletonAnalyticsOptional: () => getSingletonAnalyticsOptional,
37
+ hasGlobalAnalytics: () => hasGlobalAnalytics,
38
+ hasSingletonAnalytics: () => hasSingletonAnalytics,
39
+ removeGlobalAnalytics: () => removeGlobalAnalytics,
40
+ resetSingletonAnalytics: () => resetSingletonAnalytics,
41
+ setGlobalAnalytics: () => setGlobalAnalytics
42
+ });
43
+ module.exports = __toCommonJS(src_exports);
44
+
45
+ // src/base.ts
46
+ var BaseAnalytics = class {
47
+ constructor(config) {
48
+ var _a, _b;
49
+ this.debug = (_a = config.debug) != null ? _a : false;
50
+ this.enabled = (_b = config.enabled) != null ? _b : true;
51
+ this.business = config.business;
52
+ }
53
+ /**
54
+ * Check if provider is enabled
55
+ */
56
+ isEnabled() {
57
+ if (!this.enabled) {
58
+ this.log("Provider is disabled");
59
+ return false;
60
+ }
61
+ return true;
62
+ }
63
+ /**
64
+ * Validate event data
65
+ */
66
+ validateEvent(event) {
67
+ if (!event.name) {
68
+ this.logError("Event name is required");
69
+ return false;
70
+ }
71
+ return true;
72
+ }
73
+ /**
74
+ * Log debug message
75
+ */
76
+ log(message, data) {
77
+ if (this.debug) {
78
+ console.log(`[${this.getProviderName()}] ${message}`, data || "");
79
+ }
80
+ }
81
+ /**
82
+ * Log error message
83
+ */
84
+ logError(message, error) {
85
+ console.error(`[${this.getProviderName()}] ${message}`, error || "");
86
+ }
87
+ /**
88
+ * 丰富属性数据,自动处理 spm 前缀
89
+ * 确保无论通过哪种方式调用都会添加 business 前缀
90
+ */
91
+ enrichProperties(properties) {
92
+ const enriched = { ...properties };
93
+ enriched.business = this.business;
94
+ if (enriched.spm && typeof enriched.spm === "string" && enriched.spm.trim()) {
95
+ if (enriched.spm !== this.business && !enriched.spm.startsWith(`${this.business}.`)) {
96
+ enriched.spm = `${this.business}.${enriched.spm}`;
97
+ }
98
+ } else {
99
+ enriched.spm = this.business;
100
+ }
101
+ return enriched;
102
+ }
103
+ };
104
+
105
+ // src/manager.ts
106
+ var AnalyticsManager = class {
107
+ constructor(business, debug = false) {
108
+ this.providers = /* @__PURE__ */ new Map();
109
+ this.globalContext = {};
110
+ this.initialized = false;
111
+ this.business = business;
112
+ this.debug = debug;
113
+ }
114
+ /**
115
+ * 注册分析工具提供商
116
+ */
117
+ registerProvider(name, provider) {
118
+ this.providers.set(name, provider);
119
+ this.log(`Registered provider: ${name}`);
120
+ return this;
121
+ }
122
+ /**
123
+ * 移除分析工具提供商
124
+ */
125
+ unregisterProvider(name) {
126
+ this.providers.delete(name);
127
+ this.log(`Unregistered provider: ${name}`);
128
+ return this;
129
+ }
130
+ getProvider(name) {
131
+ return this.providers.get(name);
132
+ }
133
+ /**
134
+ * 获取所有提供商
135
+ */
136
+ getAllProviders() {
137
+ return Array.from(this.providers.values());
138
+ }
139
+ /**
140
+ * 初始化所有提供商
141
+ */
142
+ async initialize() {
143
+ if (this.initialized) {
144
+ this.log("Already initialized");
145
+ return;
146
+ }
147
+ const results = await this.executeOnAllProviders("initialize");
148
+ this.initialized = true;
149
+ this.log(`Initialized ${results.success.length}/${this.providers.size} providers`);
150
+ if (results.errors.length > 0) {
151
+ console.warn(`[AnalyticsManager] ${results.errors.length} providers failed to initialize`);
152
+ }
153
+ }
154
+ /**
155
+ * 追踪事件到所有提供商
156
+ */
157
+ async track(event) {
158
+ if (!this.ensureInitialized()) return;
159
+ const enrichedEvent = this.enrichEvent(event);
160
+ await this.executeOnAllProviders("track", enrichedEvent);
161
+ }
162
+ /**
163
+ * 类型安全的事件追踪
164
+ */
165
+ async trackEvent(eventName, properties) {
166
+ await this.track({
167
+ name: eventName,
168
+ properties
169
+ });
170
+ }
171
+ /**
172
+ * 识别用户
173
+ */
174
+ async identify(userId, properties) {
175
+ if (!this.ensureInitialized()) return;
176
+ const mergedProperties = { ...this.globalContext, ...properties };
177
+ await this.executeOnAllProviders("identify", userId, mergedProperties);
178
+ }
179
+ /**
180
+ * 追踪页面浏览
181
+ */
182
+ async trackPageView(page, properties) {
183
+ if (!this.ensureInitialized()) return;
184
+ const mergedProperties = { ...this.globalContext, ...properties };
185
+ await this.executeOnAllProviders("trackPageView", page, mergedProperties);
186
+ }
187
+ /**
188
+ * 重置用户身份
189
+ */
190
+ async reset() {
191
+ if (!this.ensureInitialized()) return;
192
+ await this.executeOnAllProviders("reset");
193
+ }
194
+ /**
195
+ * 设置全局上下文
196
+ */
197
+ setGlobalContext(context) {
198
+ this.globalContext = { ...this.globalContext, ...context };
199
+ this.log("Updated global context", this.globalContext);
200
+ return this;
201
+ }
202
+ /**
203
+ * 获取全局上下文
204
+ */
205
+ getGlobalContext() {
206
+ return { ...this.globalContext };
207
+ }
208
+ /**
209
+ * 获取管理器状态
210
+ */
211
+ getStatus() {
212
+ return {
213
+ initialized: this.initialized,
214
+ providersCount: this.providers.size
215
+ };
216
+ }
217
+ /**
218
+ * 检查是否已初始化
219
+ */
220
+ ensureInitialized() {
221
+ if (!this.initialized) {
222
+ console.warn("[AnalyticsManager] Not initialized. Call initialize() first.");
223
+ return false;
224
+ }
225
+ return true;
226
+ }
227
+ /**
228
+ * 在所有提供商上执行操作
229
+ */
230
+ async executeOnAllProviders(method, ...args) {
231
+ const results = {
232
+ errors: [],
233
+ success: []
234
+ };
235
+ const promises = Array.from(this.providers.entries()).map(async ([name, provider]) => {
236
+ try {
237
+ await provider[method](...args);
238
+ results.success.push(name);
239
+ } catch (error) {
240
+ results.errors.push({ error, provider: name });
241
+ console.error(
242
+ `[AnalyticsManager] ${method} failed for ${provider.getProviderName()}:`,
243
+ error
244
+ );
245
+ }
246
+ });
247
+ await Promise.allSettled(promises);
248
+ return results;
249
+ }
250
+ /**
251
+ * 丰富事件数据
252
+ */
253
+ enrichEvent(event) {
254
+ return {
255
+ ...event,
256
+ properties: {
257
+ ...this.globalContext,
258
+ ...event.properties
259
+ },
260
+ timestamp: event.timestamp || /* @__PURE__ */ new Date()
261
+ };
262
+ }
263
+ /**
264
+ * 记录日志
265
+ */
266
+ log(message, data) {
267
+ if (this.debug) {
268
+ console.log(`[AnalyticsManager] ${message}`, data || "");
269
+ }
270
+ }
271
+ };
272
+
273
+ // src/providers/ga4.ts
274
+ var GoogleAnalyticsProvider = class extends BaseAnalytics {
275
+ constructor(config, business) {
276
+ super({ business, debug: config.debug, enabled: config.enabled });
277
+ this.initialized = false;
278
+ this.config = config;
279
+ }
280
+ getProviderName() {
281
+ return "Google Analytics 4";
282
+ }
283
+ async initialize() {
284
+ var _a;
285
+ if (!this.isEnabled() || this.initialized) {
286
+ return;
287
+ }
288
+ try {
289
+ if (typeof window === "undefined") {
290
+ this.logError("GA4 provider requires browser environment");
291
+ return;
292
+ }
293
+ window.dataLayer = window.dataLayer || [];
294
+ const gtag = window.gtag || function() {
295
+ window.dataLayer.push(arguments);
296
+ };
297
+ window.gtag = gtag;
298
+ const existingScript = document.querySelector(
299
+ `script[src*="gtag/js"], script[id*="ga"], script[id*="gtag"]`
300
+ );
301
+ if (!existingScript) {
302
+ const script = document.createElement("script");
303
+ script.async = true;
304
+ script.src = `https://www.googletagmanager.com/gtag/js?id=${this.config.measurementId}`;
305
+ document.head.append(script);
306
+ }
307
+ gtag("js", /* @__PURE__ */ new Date());
308
+ const configOptions = {
309
+ // User's gtag config options
310
+ ...this.config.gtagConfig,
311
+ // Our internal config (these override user config for consistency)
312
+ debug_mode: this.debug || ((_a = this.config.gtagConfig) == null ? void 0 : _a.debug_mode)
313
+ };
314
+ gtag("config", this.config.measurementId, configOptions);
315
+ this.initialized = true;
316
+ this.log("Google Analytics 4 initialized successfully");
317
+ this.log(`Measurement ID: ${this.config.measurementId}`);
318
+ this.log(`Business context will be added to all events: ${this.business}`);
319
+ } catch (error) {
320
+ this.logError("Failed to initialize Google Analytics 4", error);
321
+ throw error;
322
+ }
323
+ }
324
+ async track(event) {
325
+ if (!this.isEnabled() || !this.initialized || !this.validateEvent(event)) {
326
+ return;
327
+ }
328
+ try {
329
+ const gtag = window.gtag;
330
+ if (!gtag) {
331
+ this.logError("gtag function not available");
332
+ return;
333
+ }
334
+ const enrichedProperties = this.enrichProperties(event.properties);
335
+ const eventParams = {
336
+ ...enrichedProperties,
337
+ // Add user_id if provided in the event
338
+ ...event.userId && { user_id: event.userId }
339
+ };
340
+ gtag("event", event.name, eventParams);
341
+ this.log(`Tracked event: ${event.name}`, { ...event, properties: enrichedProperties });
342
+ } catch (error) {
343
+ this.logError(`Failed to track event: ${event.name}`, error);
344
+ }
345
+ }
346
+ async identify(userId, properties) {
347
+ if (!this.isEnabled() || !this.initialized) {
348
+ return;
349
+ }
350
+ try {
351
+ const gtag = window.gtag;
352
+ if (!gtag) {
353
+ this.logError("gtag function not available");
354
+ return;
355
+ }
356
+ gtag("config", this.config.measurementId, {
357
+ user_id: userId
358
+ });
359
+ if (properties && Object.keys(properties).length > 0) {
360
+ const enrichedProperties = this.enrichProperties(properties);
361
+ gtag("set", {
362
+ user_properties: enrichedProperties
363
+ });
364
+ }
365
+ gtag("event", "login", {
366
+ user_id: userId,
367
+ ...this.enrichProperties()
368
+ });
369
+ this.log(`Identified user: ${userId}`, properties);
370
+ } catch (error) {
371
+ this.logError(`Failed to identify user: ${userId}`, error);
372
+ }
373
+ }
374
+ async trackPageView(page, properties) {
375
+ if (!this.isEnabled() || !this.initialized) {
376
+ return;
377
+ }
378
+ try {
379
+ const enrichedProperties = this.enrichProperties(properties);
380
+ await this.track({
381
+ name: "page_view",
382
+ properties: {
383
+ page_location: page,
384
+ page_title: page,
385
+ ...enrichedProperties
386
+ }
387
+ });
388
+ this.log(`Tracked page view: ${page}`, enrichedProperties);
389
+ } catch (error) {
390
+ this.logError(`Failed to track page view: ${page}`, error);
391
+ }
392
+ }
393
+ async reset() {
394
+ if (!this.isEnabled() || !this.initialized) {
395
+ return;
396
+ }
397
+ try {
398
+ const gtag = window.gtag;
399
+ if (!gtag) {
400
+ this.logError("gtag function not available");
401
+ return;
402
+ }
403
+ gtag("config", this.config.measurementId, {
404
+ user_id: null
405
+ });
406
+ gtag("set", {
407
+ user_properties: {}
408
+ });
409
+ gtag("event", "logout", this.enrichProperties());
410
+ this.log("Reset user identity and tracked logout");
411
+ } catch (error) {
412
+ this.logError("Failed to reset user identity", error);
413
+ }
414
+ }
415
+ /**
416
+ * Check if feature flag is enabled
417
+ * Note: GA4 doesn't have built-in feature flags like PostHog,
418
+ * so this always returns false
419
+ */
420
+ isFeatureEnabled(flag) {
421
+ this.log(`Feature flags not supported in GA4. Flag "${flag}" returns false`);
422
+ return false;
423
+ }
424
+ /**
425
+ * Get the native gtag function for direct access to GA4 APIs
426
+ *
427
+ * Note: When using the native gtag function directly, events will NOT automatically
428
+ * include the business and spm properties. You need to add them manually if desired.
429
+ *
430
+ * @returns gtag function or null if not initialized
431
+ *
432
+ * @example
433
+ * ```typescript
434
+ * const analytics = createAnalytics({ business: 'myapp', ... });
435
+ * const ga4Provider = analytics.getProvider('ga4');
436
+ * const gtag = ga4Provider.getNativeInstance();
437
+ *
438
+ * // Manual business context addition required for direct calls
439
+ * gtag?.('event', 'custom_event', {
440
+ * custom: 'data',
441
+ * business: 'myapp',
442
+ * spm: 'myapp.custom_section'
443
+ * });
444
+ * ```
445
+ */
446
+ getNativeInstance() {
447
+ if (!this.isEnabled() || !this.initialized) {
448
+ this.log("Cannot get native instance: provider not enabled or not initialized");
449
+ return null;
450
+ }
451
+ const gtag = window.gtag;
452
+ if (!gtag) {
453
+ this.log("gtag function not available");
454
+ return null;
455
+ }
456
+ return gtag;
457
+ }
458
+ /**
459
+ * Get current measurement ID
460
+ */
461
+ getMeasurementId() {
462
+ return this.config.measurementId;
463
+ }
464
+ /**
465
+ * Get current business context
466
+ */
467
+ getCurrentBusiness() {
468
+ return this.business;
469
+ }
470
+ };
471
+
472
+ // src/providers/posthog.ts
473
+ var import_posthog_js = require("posthog-js");
474
+ var PostHogAnalyticsProvider = class extends BaseAnalytics {
475
+ constructor(config, business) {
476
+ super({ business, debug: config.debug, enabled: config.enabled });
477
+ this.initialized = false;
478
+ this.config = config;
479
+ }
480
+ getProviderName() {
481
+ return "PostHog";
482
+ }
483
+ async initialize() {
484
+ if (!this.isEnabled() || this.initialized) {
485
+ return;
486
+ }
487
+ try {
488
+ const { key, host, ...posthogConfig } = this.config;
489
+ const initConfig = {
490
+ ...posthogConfig,
491
+ // User's posthog-js config options
492
+ api_host: host || posthogConfig.api_host || "https://app.posthog.com",
493
+ // Use before_send to dynamically add business context to all events
494
+ before_send: this.createBeforeSendHandler(posthogConfig.before_send),
495
+ debug: this.debug,
496
+ loaded: () => this.log("PostHog loaded and ready")
497
+ };
498
+ import_posthog_js.posthog.init(key, initConfig);
499
+ this.initialized = true;
500
+ this.log("PostHog initialized successfully");
501
+ this.log(`Using before_send to add business context: ${this.business}`);
502
+ } catch (error) {
503
+ this.logError("Failed to initialize PostHog", error);
504
+ throw error;
505
+ }
506
+ }
507
+ async track(event) {
508
+ if (!this.isEnabled() || !this.initialized || !this.validateEvent(event)) {
509
+ return;
510
+ }
511
+ try {
512
+ const enrichedProperties = this.enrichProperties(event.properties);
513
+ import_posthog_js.posthog.capture(event.name, {
514
+ ...enrichedProperties,
515
+ ...event.userId && { distinct_id: event.userId }
516
+ });
517
+ this.log(`Tracked event: ${event.name}`, { ...event, properties: enrichedProperties });
518
+ } catch (error) {
519
+ this.logError(`Failed to track event: ${event.name}`, error);
520
+ }
521
+ }
522
+ async identify(userId, properties) {
523
+ if (!this.isEnabled() || !this.initialized) {
524
+ return;
525
+ }
526
+ try {
527
+ const enrichedProperties = this.enrichProperties(properties);
528
+ import_posthog_js.posthog.identify(userId, enrichedProperties);
529
+ this.log(`Identified user: ${userId}`, enrichedProperties);
530
+ } catch (error) {
531
+ this.logError(`Failed to identify user: ${userId}`, error);
532
+ }
533
+ }
534
+ async trackPageView(page, properties) {
535
+ if (!this.isEnabled() || !this.initialized) {
536
+ return;
537
+ }
538
+ try {
539
+ const enrichedProperties = this.enrichProperties(properties);
540
+ await this.track({
541
+ name: "$pageview",
542
+ properties: { page, ...enrichedProperties }
543
+ });
544
+ this.log(`Tracked page view: ${page}`, enrichedProperties);
545
+ } catch (error) {
546
+ this.logError(`Failed to track page view: ${page}`, error);
547
+ }
548
+ }
549
+ async reset() {
550
+ if (!this.isEnabled() || !this.initialized) {
551
+ return;
552
+ }
553
+ try {
554
+ import_posthog_js.posthog.reset();
555
+ this.log("Reset user identity");
556
+ } catch (error) {
557
+ this.logError("Failed to reset user identity", error);
558
+ }
559
+ }
560
+ /**
561
+ * Check if feature flag is enabled
562
+ */
563
+ isFeatureEnabled(flag) {
564
+ if (!this.initialized) {
565
+ return false;
566
+ }
567
+ try {
568
+ return Boolean(import_posthog_js.posthog.isFeatureEnabled(flag));
569
+ } catch (error) {
570
+ this.logError(`Failed to check feature flag: ${flag}`, error);
571
+ return false;
572
+ }
573
+ }
574
+ /**
575
+ * Get the native PostHog instance for direct access to PostHog APIs
576
+ *
577
+ * Note: When using the native instance directly, events will still include
578
+ * the business spm prefix because it's registered as a global property.
579
+ *
580
+ * @returns PostHog native instance or null if not initialized
581
+ *
582
+ * @example
583
+ * ```typescript
584
+ * const analytics = createAnalytics({ business: 'myapp', ... });
585
+ * const posthogProvider = analytics.getProvider('posthog');
586
+ * const posthog = posthogProvider.getNativeInstance();
587
+ *
588
+ * // These calls will automatically include spm: 'myapp'
589
+ * posthog?.capture('custom_event', { custom: 'data' });
590
+ * posthog?.isFeatureEnabled('new_feature');
591
+ * posthog?.group('company', 'company_123');
592
+ * ```
593
+ */
594
+ getNativeInstance() {
595
+ if (!this.isEnabled() || !this.initialized) {
596
+ this.log("Cannot get native instance: provider not enabled or not initialized");
597
+ return null;
598
+ }
599
+ return import_posthog_js.posthog;
600
+ }
601
+ /**
602
+ * Create a before_send handler that adds business context to all events
603
+ * This ensures both wrapper calls and direct PostHog calls include business information
604
+ */
605
+ createBeforeSendHandler(userBeforeSend) {
606
+ return (event) => {
607
+ var _a;
608
+ if (!event) {
609
+ return null;
610
+ }
611
+ const originallyHadSpm = ((_a = event.properties) == null ? void 0 : _a.spm) !== void 0;
612
+ let processedEvent = event;
613
+ if (userBeforeSend) {
614
+ if (Array.isArray(userBeforeSend)) {
615
+ for (const fn of userBeforeSend) {
616
+ processedEvent = fn(processedEvent);
617
+ if (!processedEvent) {
618
+ return null;
619
+ }
620
+ }
621
+ } else if (typeof userBeforeSend === "function") {
622
+ processedEvent = userBeforeSend(processedEvent);
623
+ if (!processedEvent) {
624
+ return null;
625
+ }
626
+ }
627
+ }
628
+ if (!processedEvent.properties) {
629
+ processedEvent.properties = {};
630
+ }
631
+ processedEvent.properties.business = this.business;
632
+ const currentSpm = processedEvent.properties.spm;
633
+ const shouldSetDefaultSpm = !originallyHadSpm && (!currentSpm || typeof currentSpm === "string" && !currentSpm.trim());
634
+ if (shouldSetDefaultSpm) {
635
+ processedEvent.properties.spm = this.business;
636
+ }
637
+ return processedEvent;
638
+ };
639
+ }
640
+ /**
641
+ * Update the business context dynamically
642
+ * This will affect all future events
643
+ */
644
+ updateBusiness(newBusiness) {
645
+ if (!this.isEnabled() || !this.initialized) {
646
+ this.log("Cannot update business: provider not enabled or not initialized");
647
+ return;
648
+ }
649
+ this.log(`Business update requested: ${newBusiness} (current: ${this.business})`);
650
+ this.log("Note: Dynamic business updates require storing business in a mutable field");
651
+ }
652
+ /**
653
+ * Get current business context
654
+ */
655
+ getCurrentBusiness() {
656
+ return this.business;
657
+ }
658
+ };
659
+
660
+ // src/providers/xads.ts
661
+ var X_ADS_SCRIPT_SELECTOR = 'script[src="https://static.ads-twitter.com/uwt.js"]';
662
+ var X_ADS_SCRIPT_SRC = "https://static.ads-twitter.com/uwt.js";
663
+ var XAdsAnalyticsProvider = class extends BaseAnalytics {
664
+ constructor(config, business) {
665
+ super({ business, debug: config.debug, enabled: config.enabled });
666
+ this.initialized = false;
667
+ this.config = config;
668
+ }
669
+ getProviderName() {
670
+ return "X Ads";
671
+ }
672
+ async initialize() {
673
+ if (!this.isEnabled() || this.initialized) {
674
+ return;
675
+ }
676
+ if (typeof window === "undefined") {
677
+ this.logError("X Ads provider requires browser environment");
678
+ return;
679
+ }
680
+ if (!this.config.pixelId) {
681
+ this.logError("X Ads pixelId is required");
682
+ return;
683
+ }
684
+ try {
685
+ const twq = this.ensureTwq();
686
+ const state = this.getGlobalState();
687
+ this.ensureScript(state);
688
+ if (!state.configuredPixelIds.has(this.config.pixelId)) {
689
+ twq("config", this.config.pixelId);
690
+ state.configuredPixelIds.add(this.config.pixelId);
691
+ }
692
+ this.initialized = true;
693
+ this.log("X Ads initialized successfully");
694
+ this.log(`Pixel ID: ${this.config.pixelId}`);
695
+ } catch (error) {
696
+ this.logError("Failed to initialize X Ads", error);
697
+ throw error;
698
+ }
699
+ }
700
+ async track(event) {
701
+ if (!this.isEnabled() || !this.initialized || !this.validateEvent(event)) {
702
+ return;
703
+ }
704
+ const eventId = this.getEventId(event.name);
705
+ if (!eventId) {
706
+ this.log(`Skipping event: ${event.name} is not configured for X Ads`);
707
+ return;
708
+ }
709
+ const twq = window.twq;
710
+ if (!twq) {
711
+ this.logError("twq function not available");
712
+ return;
713
+ }
714
+ try {
715
+ const eventParams = this.getEventParameters(event);
716
+ if (eventParams) {
717
+ twq("event", eventId, eventParams);
718
+ } else {
719
+ twq("event", eventId);
720
+ }
721
+ this.log(`Tracked event: ${event.name}`, {
722
+ event: event.name,
723
+ eventId,
724
+ ...eventParams
725
+ });
726
+ } catch (error) {
727
+ this.logError(`Failed to track event: ${event.name}`, error);
728
+ }
729
+ }
730
+ async identify() {
731
+ this.log("Identify is not supported in X Ads provider");
732
+ }
733
+ async trackPageView() {
734
+ this.log("Page views are handled by the X Pixel base code");
735
+ }
736
+ async reset() {
737
+ this.log("Reset is not supported in X Ads provider");
738
+ }
739
+ ensureScript(state) {
740
+ if (state.scriptRequested || document.querySelector(X_ADS_SCRIPT_SELECTOR)) {
741
+ state.scriptRequested = true;
742
+ return;
743
+ }
744
+ const script = document.createElement("script");
745
+ script.async = true;
746
+ script.src = X_ADS_SCRIPT_SRC;
747
+ document.head.append(script);
748
+ state.scriptRequested = true;
749
+ }
750
+ ensureTwq() {
751
+ if (window.twq) {
752
+ return window.twq;
753
+ }
754
+ const twq = (...args) => {
755
+ if (twq.exe) {
756
+ twq.exe(...args);
757
+ return;
758
+ }
759
+ const queue = twq.queue || [];
760
+ queue.push(args);
761
+ twq.queue = queue;
762
+ };
763
+ twq.version = "1.1";
764
+ twq.queue = [];
765
+ window.twq = twq;
766
+ return twq;
767
+ }
768
+ getGlobalState() {
769
+ if (!window.__LOBE_ANALYTICS_X_ADS_STATE__) {
770
+ window.__LOBE_ANALYTICS_X_ADS_STATE__ = {
771
+ configuredPixelIds: /* @__PURE__ */ new Set(),
772
+ scriptRequested: false
773
+ };
774
+ }
775
+ return window.__LOBE_ANALYTICS_X_ADS_STATE__;
776
+ }
777
+ getEventId(eventName) {
778
+ var _a, _b, _c;
779
+ if (eventName === "purchase") {
780
+ return (_b = this.config.purchaseEventId) != null ? _b : (_a = this.config.eventIds) == null ? void 0 : _a[eventName];
781
+ }
782
+ return (_c = this.config.eventIds) == null ? void 0 : _c[eventName];
783
+ }
784
+ getEventParameters(event) {
785
+ if (event.name === "purchase") {
786
+ return this.mapPurchaseEventParameters(event.properties);
787
+ }
788
+ return void 0;
789
+ }
790
+ mapPurchaseEventParameters(properties) {
791
+ const eventParams = {};
792
+ const currency = properties == null ? void 0 : properties.currency;
793
+ if (typeof currency === "string" && currency.trim()) {
794
+ eventParams.currency = currency.trim().toUpperCase();
795
+ }
796
+ const value = this.toFiniteNumber(properties == null ? void 0 : properties.value);
797
+ if (value !== void 0) {
798
+ eventParams.value = value;
799
+ }
800
+ const transactionId = properties == null ? void 0 : properties.transaction_id;
801
+ if (typeof transactionId === "string" || typeof transactionId === "number") {
802
+ eventParams.conversion_id = String(transactionId);
803
+ }
804
+ const contents = this.mapContents(properties == null ? void 0 : properties.items);
805
+ if (contents.length > 0) {
806
+ eventParams.contents = contents;
807
+ }
808
+ return eventParams;
809
+ }
810
+ mapContents(items) {
811
+ if (!Array.isArray(items)) {
812
+ return [];
813
+ }
814
+ const contents = items.map((item) => {
815
+ if (!item || typeof item !== "object") {
816
+ return null;
817
+ }
818
+ const content = item;
819
+ const contentItem = {};
820
+ const contentPrice = this.toFiniteNumber(content.price);
821
+ const numItems = this.toFiniteNumber(content.quantity);
822
+ if (typeof content.item_id === "string" && content.item_id.trim()) {
823
+ contentItem.content_id = content.item_id;
824
+ }
825
+ if (typeof content.item_name === "string" && content.item_name.trim()) {
826
+ contentItem.content_name = content.item_name;
827
+ }
828
+ if (contentPrice !== void 0) {
829
+ contentItem.content_price = contentPrice;
830
+ }
831
+ if (numItems !== void 0) {
832
+ contentItem.num_items = numItems;
833
+ }
834
+ return Object.keys(contentItem).length > 0 ? contentItem : null;
835
+ }).filter((content) => content !== null);
836
+ return contents;
837
+ }
838
+ toFiniteNumber(value) {
839
+ if (typeof value === "number" && Number.isFinite(value)) {
840
+ return value;
841
+ }
842
+ if (typeof value === "string" && value.trim()) {
843
+ const parsed = Number(value);
844
+ if (Number.isFinite(parsed)) {
845
+ return parsed;
846
+ }
847
+ }
848
+ return void 0;
849
+ }
850
+ };
851
+
852
+ // src/config.ts
853
+ function createAnalytics(config) {
854
+ var _a, _b, _c, _d;
855
+ const manager = new AnalyticsManager(config.business, config.debug);
856
+ if ((_a = config.providers.posthog) == null ? void 0 : _a.enabled) {
857
+ const provider = new PostHogAnalyticsProvider(config.providers.posthog, config.business);
858
+ manager.registerProvider("posthog", provider);
859
+ }
860
+ if ((_b = config.providers.ga4) == null ? void 0 : _b.enabled) {
861
+ const provider = new GoogleAnalyticsProvider(config.providers.ga4, config.business);
862
+ manager.registerProvider("ga4", provider);
863
+ }
864
+ if ((_c = config.providers.xAds) == null ? void 0 : _c.enabled) {
865
+ const provider = new XAdsAnalyticsProvider(config.providers.xAds, config.business);
866
+ manager.registerProvider("xAds", provider);
867
+ }
868
+ if ((_d = config.providers.posthogNode) == null ? void 0 : _d.enabled) {
869
+ console.warn(
870
+ 'PostHog Node.js provider is not available in the client entry point. Please use "@lobehub/analytics/server" for server-side analytics.'
871
+ );
872
+ }
873
+ return manager;
874
+ }
875
+
876
+ // src/global.ts
877
+ var GLOBAL_STATE_KEY = "__LOBE_ANALYTICS_GLOBAL_STATE__";
878
+ function getGlobalState() {
879
+ if (!globalThis[GLOBAL_STATE_KEY]) {
880
+ globalThis[GLOBAL_STATE_KEY] = {
881
+ instances: /* @__PURE__ */ new Map(),
882
+ singletonConfig: null,
883
+ singletonInstance: null
884
+ };
885
+ }
886
+ return globalThis[GLOBAL_STATE_KEY];
887
+ }
888
+ var DEFAULT_INSTANCE_NAME = "__default__";
889
+ function setGlobalAnalytics(instance, name = DEFAULT_INSTANCE_NAME) {
890
+ const state = getGlobalState();
891
+ state.instances.set(name, instance);
892
+ }
893
+ function getGlobalAnalytics(name = DEFAULT_INSTANCE_NAME) {
894
+ const state = getGlobalState();
895
+ const instance = state.instances.get(name);
896
+ if (!instance) {
897
+ throw new Error(
898
+ `Global analytics instance "${name}" not found. Make sure to register it first using setGlobalAnalytics() or use AnalyticsProvider.`
899
+ );
900
+ }
901
+ return instance;
902
+ }
903
+ function getGlobalAnalyticsOptional(name = DEFAULT_INSTANCE_NAME) {
904
+ const state = getGlobalState();
905
+ return state.instances.get(name) || null;
906
+ }
907
+ function hasGlobalAnalytics(name = DEFAULT_INSTANCE_NAME) {
908
+ const state = getGlobalState();
909
+ return state.instances.has(name);
910
+ }
911
+ function removeGlobalAnalytics(name = DEFAULT_INSTANCE_NAME) {
912
+ const state = getGlobalState();
913
+ return state.instances.delete(name);
914
+ }
915
+ function clearGlobalAnalytics() {
916
+ const state = getGlobalState();
917
+ state.instances.clear();
918
+ }
919
+ function getGlobalAnalyticsNames() {
920
+ const state = getGlobalState();
921
+ return Array.from(state.instances.keys());
922
+ }
923
+ function isConfigEqual(config1, config2) {
924
+ try {
925
+ return JSON.stringify(config1) === JSON.stringify(config2);
926
+ } catch (e) {
927
+ return false;
928
+ }
929
+ }
930
+ function createSingletonAnalytics(config) {
931
+ const state = getGlobalState();
932
+ if (state.singletonInstance && state.singletonConfig && isConfigEqual(state.singletonConfig, config)) {
933
+ return state.singletonInstance;
934
+ }
935
+ state.singletonInstance = createAnalytics(config);
936
+ state.singletonConfig = config;
937
+ setGlobalAnalytics(state.singletonInstance);
938
+ return state.singletonInstance;
939
+ }
940
+ function getSingletonAnalytics() {
941
+ const state = getGlobalState();
942
+ if (!state.singletonInstance) {
943
+ throw new Error(
944
+ "Singleton analytics instance not created. Call createSingletonAnalytics() first or use getGlobalAnalytics()."
945
+ );
946
+ }
947
+ return state.singletonInstance;
948
+ }
949
+ function getSingletonAnalyticsOptional() {
950
+ const state = getGlobalState();
951
+ return state.singletonInstance;
952
+ }
953
+ function hasSingletonAnalytics() {
954
+ const state = getGlobalState();
955
+ return state.singletonInstance !== null;
956
+ }
957
+ function resetSingletonAnalytics() {
958
+ const state = getGlobalState();
959
+ state.singletonInstance = null;
960
+ state.singletonConfig = null;
961
+ }
962
+ // Annotate the CommonJS export names for ESM import in node:
963
+ 0 && (module.exports = {
964
+ AnalyticsManager,
965
+ BaseAnalytics,
966
+ GoogleAnalyticsProvider,
967
+ PostHogAnalyticsProvider,
968
+ XAdsAnalyticsProvider,
969
+ clearGlobalAnalytics,
970
+ createAnalytics,
971
+ createSingletonAnalytics,
972
+ getGlobalAnalytics,
973
+ getGlobalAnalyticsNames,
974
+ getGlobalAnalyticsOptional,
975
+ getSingletonAnalytics,
976
+ getSingletonAnalyticsOptional,
977
+ hasGlobalAnalytics,
978
+ hasSingletonAnalytics,
979
+ removeGlobalAnalytics,
980
+ resetSingletonAnalytics,
981
+ setGlobalAnalytics
982
+ });
983
+ //# sourceMappingURL=index.js.map