@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 +1 -1
- package/package.json +1 -1
- package/src/lib/ads.ts +63 -8
- package/src/lib/thead-vantage-config.ts +7 -4
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
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
|
-
//
|
|
176
|
+
// Extract the redirect URL
|
|
175
177
|
const redirectUrl = location.startsWith('http')
|
|
176
178
|
? location
|
|
177
179
|
: new URL(location, normalizedUrl).toString();
|
|
178
|
-
|
|
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
|
-
|
|
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
|
-
|
|
307
|
-
|
|
308
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|