@thead-vantage/react 2.11.0 → 2.13.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/README.md CHANGED
@@ -31,7 +31,7 @@ The TheAd Vantage integration supports multiple modes of operation to accommodat
31
31
  **Use Case**: Platform developers using the component in production deployments.
32
32
 
33
33
  **Behavior**:
34
- - Connects to `https://thead-vantage.com/api/ads`
34
+ - Connects to `https://www.thead-vantage.com/api/ads` (uses www to avoid 308 redirects)
35
35
  - Full tracking enabled (impressions and clicks are recorded)
36
36
  - No configuration required
37
37
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thead-vantage/react",
3
- "version": "2.11.0",
3
+ "version": "2.13.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",
package/src/lib/ads.ts CHANGED
@@ -168,16 +168,26 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
168
168
  });
169
169
 
170
170
  // Handle redirects manually (if any)
171
+ // Note: 308 redirects at Vercel edge level will still break CORS preflight
172
+ // This handles redirects for the actual GET request, but preflight redirects are blocked by browser
171
173
  if (response.type === 'opaqueredirect' || (response.status >= 300 && response.status < 400)) {
172
174
  const location = response.headers.get('location');
173
175
  if (location) {
174
- // Follow the redirect for the actual request (not preflight)
176
+ // Extract the redirect URL
175
177
  const redirectUrl = location.startsWith('http')
176
178
  ? location
177
179
  : new URL(location, normalizedUrl).toString();
178
- console.warn('[AdBanner] Server redirected request, following redirect:', redirectUrl.replace(new RegExp(`api_key=${params.apiKey}`, 'g'), 'api_key=***'));
180
+
181
+ console.warn('[AdBanner] Server redirected request (status:', response.status, '), following redirect:', redirectUrl.replace(new RegExp(`api_key=${params.apiKey}`, 'g'), 'api_key=***'));
182
+ console.warn('[AdBanner] Note: If this is a 308 redirect, it may break CORS preflight. Consider using the canonical domain directly.');
183
+
179
184
  // Recursively call with the redirect URL
180
- return await fetchAdBanner({ ...params, apiUrl: redirectUrl });
185
+ // Limit recursion to prevent infinite loops
186
+ const redirectCount = (params as any).__redirectCount || 0;
187
+ if (redirectCount >= 3) {
188
+ throw new Error('Too many redirects. The server may be misconfigured.');
189
+ }
190
+ return await fetchAdBanner({ ...params, apiUrl: redirectUrl, __redirectCount: redirectCount + 1 } as any);
181
191
  }
182
192
  }
183
193
 
@@ -303,9 +313,22 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
303
313
  } catch (error) {
304
314
  // Handle CORS errors specifically
305
315
  if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
306
- console.error('[AdBanner] CORS error detected. This usually means the server is redirecting preflight requests.');
307
- console.error('[AdBanner] The server at thead-vantage.com needs to handle OPTIONS requests directly without redirecting.');
308
- throw new Error('CORS error: The ad server is redirecting preflight requests. This needs to be fixed server-side. See CORS_CONFIGURATION.md for details.');
316
+ // Check if this might be a 308 redirect issue
317
+ // Vercel redirects thead-vantage.com www.thead-vantage.com at edge level
318
+ // This breaks CORS preflight because browsers don't allow redirects on OPTIONS requests
319
+ const isRedirectIssue = normalizedUrl.includes('thead-vantage.com') && !normalizedUrl.includes('www.');
320
+
321
+ if (isRedirectIssue) {
322
+ console.error('[AdBanner] CORS error detected. This appears to be a 308 redirect issue.');
323
+ console.error('[AdBanner] The domain thead-vantage.com redirects to www.thead-vantage.com at Vercel edge level.');
324
+ console.error('[AdBanner] This breaks CORS preflight requests because browsers block redirects on OPTIONS requests.');
325
+ console.error('[AdBanner] Solution: Use www.thead-vantage.com directly (library has been updated to do this).');
326
+ throw new Error('CORS error: 308 redirect detected. The server redirects thead-vantage.com → www.thead-vantage.com, which breaks CORS preflight. The library now uses www.thead-vantage.com by default. If you see this error, check your API URL configuration.');
327
+ } else {
328
+ console.error('[AdBanner] CORS error detected. This usually means the server is redirecting preflight requests.');
329
+ console.error('[AdBanner] The server needs to handle OPTIONS requests directly without redirecting.');
330
+ throw new Error('CORS error: The ad server is redirecting preflight requests. This needs to be fixed server-side. See CORS_CONFIGURATION.md for details.');
331
+ }
309
332
  }
310
333
 
311
334
  console.error('[AdBanner] Error fetching ad:', error);
@@ -355,6 +378,15 @@ export async function trackImpression(adId: string): Promise<void> {
355
378
  trackingUrl = trackingUrl.replace(/\/$/, '') + '/api/ads/track';
356
379
  }
357
380
 
381
+ // Normalize URL to HTTPS and ensure www (avoid 308 redirects)
382
+ if (trackingUrl.startsWith('http://')) {
383
+ trackingUrl = trackingUrl.replace('http://', 'https://');
384
+ }
385
+ // Ensure www to avoid redirects
386
+ if (trackingUrl.includes('thead-vantage.com') && !trackingUrl.includes('www.')) {
387
+ trackingUrl = trackingUrl.replace('thead-vantage.com', 'www.thead-vantage.com');
388
+ }
389
+
358
390
  // Check if we should skip tracking (dev mode)
359
391
  const useDevFlags = shouldUseDevFlags();
360
392
  if (useDevFlags) {
@@ -364,6 +396,8 @@ export async function trackImpression(adId: string): Promise<void> {
364
396
 
365
397
  const response = await fetch(trackingUrl, {
366
398
  method: 'POST',
399
+ mode: 'cors', // Explicitly enable CORS
400
+ credentials: 'omit', // Don't send cookies
367
401
  headers: {
368
402
  'Content-Type': 'application/json',
369
403
  'Accept': 'application/json',
@@ -384,7 +418,12 @@ export async function trackImpression(adId: string): Promise<void> {
384
418
  console.log(`[DEV] Impression tracking skipped for ad: ${adId}`);
385
419
  }
386
420
  } catch (error) {
387
- console.error('Error tracking impression:', error);
421
+ // Handle CORS errors silently (tracking failures shouldn't break the app)
422
+ if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
423
+ console.warn(`[AdBanner] CORS error tracking impression for ad: ${adId}. The tracking endpoint needs CORS headers configured.`);
424
+ } else {
425
+ console.error('Error tracking impression:', error);
426
+ }
388
427
  // Don't throw - tracking failures shouldn't break the app
389
428
  }
390
429
  }
@@ -407,6 +446,15 @@ export async function trackClick(adId: string): Promise<void> {
407
446
  trackingUrl = trackingUrl.replace(/\/$/, '') + '/api/ads/track';
408
447
  }
409
448
 
449
+ // Normalize URL to HTTPS and ensure www (avoid 308 redirects)
450
+ if (trackingUrl.startsWith('http://')) {
451
+ trackingUrl = trackingUrl.replace('http://', 'https://');
452
+ }
453
+ // Ensure www to avoid redirects
454
+ if (trackingUrl.includes('thead-vantage.com') && !trackingUrl.includes('www.')) {
455
+ trackingUrl = trackingUrl.replace('thead-vantage.com', 'www.thead-vantage.com');
456
+ }
457
+
410
458
  // Check if we should skip tracking (dev mode)
411
459
  const useDevFlags = shouldUseDevFlags();
412
460
  if (useDevFlags) {
@@ -416,6 +464,8 @@ export async function trackClick(adId: string): Promise<void> {
416
464
 
417
465
  const response = await fetch(trackingUrl, {
418
466
  method: 'POST',
467
+ mode: 'cors', // Explicitly enable CORS
468
+ credentials: 'omit', // Don't send cookies
419
469
  headers: {
420
470
  'Content-Type': 'application/json',
421
471
  'Accept': 'application/json',
@@ -436,7 +486,12 @@ export async function trackClick(adId: string): Promise<void> {
436
486
  console.log(`[DEV] Click tracking skipped for ad: ${adId}`);
437
487
  }
438
488
  } catch (error) {
439
- console.error('Error tracking click:', error);
489
+ // Handle CORS errors silently (tracking failures shouldn't break the app)
490
+ if (error instanceof TypeError && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
491
+ console.warn(`[AdBanner] CORS error tracking click for ad: ${adId}. The tracking endpoint needs CORS headers configured.`);
492
+ } else {
493
+ console.error('Error tracking click:', error);
494
+ }
440
495
  // Don't throw - tracking failures shouldn't break the app
441
496
  }
442
497
  }
@@ -85,16 +85,19 @@ export function getApiBaseUrl(explicitApiUrl?: string): string {
85
85
 
86
86
  // Priority 4: Localhost development (platform developers on localhost)
87
87
  // Use production API but will add dev flags to prevent tracking
88
+ // Use www.thead-vantage.com to avoid 308 redirects
88
89
  if (!selectedUrl && isLocalhost()) {
89
- selectedUrl = 'https://thead-vantage.com/api/ads';
90
- reason = 'localhost detection (production API with dev flags)';
90
+ selectedUrl = 'https://www.thead-vantage.com/api/ads';
91
+ reason = 'localhost detection (production API with dev flags - using www to avoid redirects)';
91
92
  }
92
93
 
93
94
  // Priority 5: Production mode (default)
94
95
  // Platform developers use this by default in production
96
+ // IMPORTANT: Use www.thead-vantage.com to avoid 308 redirects at Vercel edge
97
+ // Vercel redirects thead-vantage.com → www.thead-vantage.com, which breaks CORS preflight
95
98
  if (!selectedUrl) {
96
- selectedUrl = 'https://thead-vantage.com/api/ads';
97
- reason = 'production mode (default)';
99
+ selectedUrl = 'https://www.thead-vantage.com/api/ads';
100
+ reason = 'production mode (default - using www to avoid redirects)';
98
101
  }
99
102
 
100
103
  // Normalize the URL to avoid redirects that cause CORS preflight issues