@thead-vantage/react 2.16.0 → 2.18.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thead-vantage/react",
3
- "version": "2.16.0",
3
+ "version": "2.18.0",
4
4
  "description": "React components and utilities for TheAd Vantage ad platform integration",
5
5
  "main": "./src/index.ts",
6
6
  "module": "./src/index.ts",
@@ -12,6 +12,8 @@ export interface AdBannerProps {
12
12
  userId?: string | null;
13
13
  userSegment?: string | null;
14
14
  className?: string;
15
+ clickMetadata?: Record<string, unknown>; // Optional metadata to send with click events
16
+ impressionMetadata?: Record<string, unknown>; // Optional metadata to send with impression events
15
17
  }
16
18
 
17
19
  export function AdBanner({
@@ -22,6 +24,8 @@ export function AdBanner({
22
24
  userId = null,
23
25
  userSegment = null,
24
26
  className = '',
27
+ clickMetadata,
28
+ impressionMetadata,
25
29
  }: AdBannerProps) {
26
30
  const [ad, setAd] = useState<Ad | null>(null);
27
31
  const [loading, setLoading] = useState(true);
@@ -43,22 +47,27 @@ export function AdBanner({
43
47
  userSegment,
44
48
  });
45
49
 
46
- console.log('[AdBanner] Processed response:', {
47
- success: response.success,
48
- hasAd: !!response.ad,
49
- devMode: response.dev_mode,
50
- message: response.message,
51
- _dev_note: response._dev_note,
52
- fullResponse: response,
53
- });
50
+ // Only log in development mode
51
+ if (process.env.NODE_ENV === 'development') {
52
+ console.log('[AdBanner] Processed response:', {
53
+ success: response.success,
54
+ hasAd: !!response.ad,
55
+ devMode: response.dev_mode,
56
+ message: response.message,
57
+ _dev_note: response._dev_note,
58
+ fullResponse: response,
59
+ });
60
+ }
54
61
 
55
62
  if (response.success && response.ad) {
56
- console.log('[AdBanner] Setting ad:', response.ad);
63
+ if (process.env.NODE_ENV === 'development') {
64
+ console.log('[AdBanner] Setting ad:', response.ad);
65
+ }
57
66
  setAd(response.ad);
58
67
  setDevMode(response.dev_mode || false);
59
68
 
60
69
  // Track impression (will be skipped in dev mode)
61
- trackImpression(response.ad.id, apiKey, apiUrl);
70
+ trackImpression(response.ad.id, apiKey, apiUrl, impressionMetadata);
62
71
  } else {
63
72
  // Create a more detailed error message
64
73
  const errorDetails = [];
@@ -106,7 +115,7 @@ export function AdBanner({
106
115
 
107
116
  const handleClick = () => {
108
117
  if (ad) {
109
- trackClick(ad.id, apiKey, apiUrl);
118
+ trackClick(ad.id, apiKey, apiUrl, clickMetadata);
110
119
  // The link will handle navigation
111
120
  }
112
121
  };
package/src/lib/ads.ts CHANGED
@@ -3,7 +3,17 @@
3
3
  * Automatically handles development mode to prevent tracking
4
4
  */
5
5
 
6
- import { getApiBaseUrl, shouldUseDevFlags } from './thead-vantage-config';
6
+ import { getApiBaseUrl, shouldUseDevFlags, isDevelopmentMode } from './thead-vantage-config';
7
+
8
+ /**
9
+ * Helper function to log only in development mode
10
+ * Errors and warnings are always logged
11
+ */
12
+ function devLog(...args: unknown[]): void {
13
+ if (process.env.NODE_ENV === 'development' || isDevelopmentMode()) {
14
+ console.log(...args);
15
+ }
16
+ }
7
17
 
8
18
  export interface AdData {
9
19
  id: string;
@@ -147,7 +157,7 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
147
157
 
148
158
  // Log the request (without exposing the API key)
149
159
  const logUrl = url.replace(new RegExp(`api_key=${params.apiKey}`, 'g'), 'api_key=***');
150
- console.log('[AdBanner] Fetching ad from:', logUrl);
160
+ devLog('[AdBanner] Fetching ad from:', logUrl);
151
161
 
152
162
  // Make direct request to thead-vantage.com
153
163
  // Try to make a "simple request" that doesn't trigger preflight
@@ -205,8 +215,8 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
205
215
  const data: AdsResponse = await response.json();
206
216
 
207
217
  // Debug logging - log the FULL response to see exactly what we're getting
208
- console.log('[AdBanner] Full API Response:', JSON.stringify(data, null, 2));
209
- console.log('[AdBanner] API Response Summary:', {
218
+ devLog('[AdBanner] Full API Response:', JSON.stringify(data, null, 2));
219
+ devLog('[AdBanner] API Response Summary:', {
210
220
  success: data.success,
211
221
  hasAd: !!data.ad,
212
222
  hasAds: !!(data.ads && Array.isArray(data.ads)),
@@ -246,19 +256,19 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
246
256
 
247
257
  // Handle both single ad and array of ads
248
258
  if (data.ads && Array.isArray(data.ads) && data.ads.length > 0) {
249
- // If we get an array, use the first ad and ensure it's Ad type
250
- const firstAd = data.ads[0];
251
- console.log('[AdBanner] Processing ads array, first ad:', JSON.stringify(firstAd, null, 2));
252
-
253
- // Check if it's already an Ad, otherwise convert from AdData
254
- let ad: Ad = isAd(firstAd)
255
- ? firstAd
256
- : convertToAd(firstAd as AdData);
257
-
258
- // Normalize the type (standard -> image)
259
- ad = normalizeAdType(ad);
260
-
261
- console.log('[AdBanner] Converted ad:', JSON.stringify(ad, null, 2));
259
+ // If we get an array, use the first ad and ensure it's Ad type
260
+ const firstAd = data.ads[0];
261
+ devLog('[AdBanner] Processing ads array, first ad:', JSON.stringify(firstAd, null, 2));
262
+
263
+ // Check if it's already an Ad, otherwise convert from AdData
264
+ let ad: Ad = isAd(firstAd)
265
+ ? firstAd
266
+ : convertToAd(firstAd as AdData);
267
+
268
+ // Normalize the type (standard -> image)
269
+ ad = normalizeAdType(ad);
270
+
271
+ devLog('[AdBanner] Converted ad:', JSON.stringify(ad, null, 2));
262
272
 
263
273
  return {
264
274
  success: data.success,
@@ -271,19 +281,19 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
271
281
 
272
282
  // Handle single ad response - convert AdData to Ad
273
283
  if (data.ad) {
274
- console.log('[AdBanner] Processing single ad:', JSON.stringify(data.ad, null, 2));
275
-
276
- // Check if it's already in Ad format or needs conversion
277
- let ad: Ad;
278
- if (isAd(data.ad)) {
279
- // Already in Ad format, normalize the type
280
- ad = normalizeAdType(data.ad);
281
- } else {
282
- // Convert from AdData format
283
- ad = convertToAd(data.ad);
284
- }
285
-
286
- console.log('[AdBanner] Converted ad:', JSON.stringify(ad, null, 2));
284
+ devLog('[AdBanner] Processing single ad:', JSON.stringify(data.ad, null, 2));
285
+
286
+ // Check if it's already in Ad format or needs conversion
287
+ let ad: Ad;
288
+ if (isAd(data.ad)) {
289
+ // Already in Ad format, normalize the type
290
+ ad = normalizeAdType(data.ad);
291
+ } else {
292
+ // Convert from AdData format
293
+ ad = convertToAd(data.ad);
294
+ }
295
+
296
+ devLog('[AdBanner] Converted ad:', JSON.stringify(ad, null, 2));
287
297
 
288
298
  return {
289
299
  success: data.success,
@@ -420,8 +430,9 @@ export function collectFingerprint(): Fingerprint {
420
430
  * @param adId - The ID of the ad being tracked
421
431
  * @param apiKey - The API key for the platform (required for CORS validation and platform identification)
422
432
  * @param apiUrl - Optional API base URL override
433
+ * @param metadata - Optional metadata object to send with the impression event
423
434
  */
424
- export async function trackImpression(adId: string, apiKey: string, apiUrl?: string): Promise<void> {
435
+ export async function trackImpression(adId: string, apiKey: string, apiUrl?: string, metadata?: Record<string, unknown>): Promise<void> {
425
436
  try {
426
437
  if (!apiKey) {
427
438
  console.warn('[AdBanner] Cannot track impression: API key is required');
@@ -456,7 +467,7 @@ export async function trackImpression(adId: string, apiKey: string, apiUrl?: str
456
467
  // Check if we should skip tracking (dev mode)
457
468
  const useDevFlags = shouldUseDevFlags();
458
469
  if (useDevFlags) {
459
- console.log(`[DEV] Impression tracking skipped for ad: ${adId}`);
470
+ devLog(`[DEV] Impression tracking skipped for ad: ${adId}`);
460
471
  return;
461
472
  }
462
473
 
@@ -472,6 +483,7 @@ export async function trackImpression(adId: string, apiKey: string, apiUrl?: str
472
483
  action: 'impression',
473
484
  adId,
474
485
  fingerprint: collectFingerprint(), // Include fingerprint data for fraud detection
486
+ ...(metadata && { metadata }), // Include metadata if provided
475
487
  }),
476
488
  });
477
489
 
@@ -485,16 +497,16 @@ export async function trackImpression(adId: string, apiKey: string, apiUrl?: str
485
497
 
486
498
  const data = await response.json();
487
499
  if (data.dev_mode) {
488
- console.log(`[DEV] Impression tracking skipped for ad: ${adId}`);
500
+ devLog(`[DEV] Impression tracking skipped for ad: ${adId}`);
489
501
  } else {
490
- console.debug(`[AdBanner] Impression tracked successfully for ad: ${adId}`);
502
+ devLog(`[AdBanner] Impression tracked successfully for ad: ${adId}`);
491
503
  }
492
504
  } catch (error) {
493
- // Handle CORS errors gracefully (tracking failures shouldn't break the app)
494
- if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
495
- // This is expected until the server configures CORS for /api/ads/track endpoint
496
- // The ad still displays correctly, tracking just won't work until server-side CORS is fixed
497
- console.debug(`[AdBanner] Tracking impression failed (CORS): The /api/ads/track endpoint needs CORS headers configured on the server. Ad still displayed successfully.`);
505
+ // Handle CORS errors gracefully (tracking failures shouldn't break the app)
506
+ if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
507
+ // This is expected until the server configures CORS for /api/ads/track endpoint
508
+ // The ad still displays correctly, tracking just won't work until server-side CORS is fixed
509
+ devLog(`[AdBanner] Tracking impression failed (CORS): The /api/ads/track endpoint needs CORS headers configured on the server. Ad still displayed successfully.`);
498
510
  } else {
499
511
  console.error('Error tracking impression:', error);
500
512
  }
@@ -509,8 +521,9 @@ export async function trackImpression(adId: string, apiKey: string, apiUrl?: str
509
521
  * @param adId - The ID of the ad being tracked
510
522
  * @param apiKey - The API key for the platform (required for CORS validation and platform identification)
511
523
  * @param apiUrl - Optional API base URL override
524
+ * @param metadata - Optional metadata object to send with the click event
512
525
  */
513
- export async function trackClick(adId: string, apiKey: string, apiUrl?: string): Promise<void> {
526
+ export async function trackClick(adId: string, apiKey: string, apiUrl?: string, metadata?: Record<string, unknown>): Promise<void> {
514
527
  try {
515
528
  if (!apiKey) {
516
529
  console.warn('[AdBanner] Cannot track click: API key is required');
@@ -545,7 +558,7 @@ export async function trackClick(adId: string, apiKey: string, apiUrl?: string):
545
558
  // Check if we should skip tracking (dev mode)
546
559
  const useDevFlags = shouldUseDevFlags();
547
560
  if (useDevFlags) {
548
- console.log(`[DEV] Click tracking skipped for ad: ${adId}`);
561
+ devLog(`[DEV] Click tracking skipped for ad: ${adId}`);
549
562
  return;
550
563
  }
551
564
 
@@ -561,6 +574,7 @@ export async function trackClick(adId: string, apiKey: string, apiUrl?: string):
561
574
  action: 'click',
562
575
  adId,
563
576
  fingerprint: collectFingerprint(), // Include fingerprint data for fraud detection
577
+ ...(metadata && { metadata }), // Include metadata if provided
564
578
  }),
565
579
  });
566
580
 
@@ -574,16 +588,16 @@ export async function trackClick(adId: string, apiKey: string, apiUrl?: string):
574
588
 
575
589
  const data = await response.json();
576
590
  if (data.dev_mode) {
577
- console.log(`[DEV] Click tracking skipped for ad: ${adId}`);
591
+ devLog(`[DEV] Click tracking skipped for ad: ${adId}`);
578
592
  } else {
579
- console.debug(`[AdBanner] Click tracked successfully for ad: ${adId}`);
593
+ devLog(`[AdBanner] Click tracked successfully for ad: ${adId}`);
580
594
  }
581
595
  } catch (error) {
582
596
  // Handle CORS errors gracefully (tracking failures shouldn't break the app)
583
597
  if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
584
598
  // This is expected until the server configures CORS for /api/ads/track endpoint
585
599
  // The ad still works correctly, click tracking just won't work until server-side CORS is fixed
586
- console.debug(`[AdBanner] Tracking click failed (CORS): The /api/ads/track endpoint needs CORS headers configured on the server. Ad still works correctly.`);
600
+ devLog(`[AdBanner] Tracking click failed (CORS): The /api/ads/track endpoint needs CORS headers configured on the server. Ad still works correctly.`);
587
601
  } else {
588
602
  console.error('Error tracking click:', error);
589
603
  }
@@ -116,15 +116,9 @@ export function getApiBaseUrl(explicitApiUrl?: string): string {
116
116
  }
117
117
 
118
118
  // Log which URL was selected (helpful for debugging)
119
- // Always log in browser context, but only in development for server-side
120
- if (typeof window !== 'undefined') {
121
- if (process.env.NODE_ENV === 'development') {
122
- console.log(`[TheAd Vantage] API Base URL selected: ${selectedUrl} (reason: ${reason})`);
123
- } else {
124
- // In production, log to console but only if there's an issue (helps with debugging)
125
- // We'll log errors separately, but this helps identify configuration issues
126
- console.debug(`[TheAd Vantage] API Base URL: ${selectedUrl}`);
127
- }
119
+ // Only log in development mode
120
+ if (typeof window !== 'undefined' && (process.env.NODE_ENV === 'development' || isDevelopmentMode())) {
121
+ console.log(`[TheAd Vantage] API Base URL selected: ${selectedUrl} (reason: ${reason})`);
128
122
  }
129
123
 
130
124
  return selectedUrl;