@thead-vantage/react 2.6.0 → 2.8.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 +1 -1
- package/src/components/AdBanner.tsx +1 -1
- package/src/lib/ads.ts +77 -7
- package/src/lib/thead-vantage-config.ts +49 -18
package/package.json
CHANGED
package/src/lib/ads.ts
CHANGED
|
@@ -183,6 +183,14 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
|
|
|
183
183
|
};
|
|
184
184
|
};
|
|
185
185
|
|
|
186
|
+
// Helper to normalize ad type - "standard" should be treated as "image"
|
|
187
|
+
const normalizeAdType = (ad: Ad): Ad => {
|
|
188
|
+
if (ad.type === 'standard' && ad.contentUrl) {
|
|
189
|
+
return { ...ad, type: 'image' };
|
|
190
|
+
}
|
|
191
|
+
return ad;
|
|
192
|
+
};
|
|
193
|
+
|
|
186
194
|
// Handle both single ad and array of ads
|
|
187
195
|
if (data.ads && Array.isArray(data.ads) && data.ads.length > 0) {
|
|
188
196
|
// If we get an array, use the first ad and ensure it's Ad type
|
|
@@ -190,10 +198,13 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
|
|
|
190
198
|
console.log('[AdBanner] Processing ads array, first ad:', JSON.stringify(firstAd, null, 2));
|
|
191
199
|
|
|
192
200
|
// Check if it's already an Ad, otherwise convert from AdData
|
|
193
|
-
|
|
201
|
+
let ad: Ad = isAd(firstAd)
|
|
194
202
|
? firstAd
|
|
195
203
|
: convertToAd(firstAd as AdData);
|
|
196
204
|
|
|
205
|
+
// Normalize the type (standard -> image)
|
|
206
|
+
ad = normalizeAdType(ad);
|
|
207
|
+
|
|
197
208
|
console.log('[AdBanner] Converted ad:', JSON.stringify(ad, null, 2));
|
|
198
209
|
|
|
199
210
|
return {
|
|
@@ -206,10 +217,19 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
|
|
|
206
217
|
}
|
|
207
218
|
|
|
208
219
|
// Handle single ad response - convert AdData to Ad
|
|
209
|
-
// Since data.ad is typed as AdData, we always convert it
|
|
210
220
|
if (data.ad) {
|
|
211
221
|
console.log('[AdBanner] Processing single ad:', JSON.stringify(data.ad, null, 2));
|
|
212
|
-
|
|
222
|
+
|
|
223
|
+
// Check if it's already in Ad format or needs conversion
|
|
224
|
+
let ad: Ad;
|
|
225
|
+
if (isAd(data.ad)) {
|
|
226
|
+
// Already in Ad format, normalize the type
|
|
227
|
+
ad = normalizeAdType(data.ad);
|
|
228
|
+
} else {
|
|
229
|
+
// Convert from AdData format
|
|
230
|
+
ad = convertToAd(data.ad);
|
|
231
|
+
}
|
|
232
|
+
|
|
213
233
|
console.log('[AdBanner] Converted ad:', JSON.stringify(ad, null, 2));
|
|
214
234
|
|
|
215
235
|
return {
|
|
@@ -264,14 +284,34 @@ export async function fetchAdBanner(params: FetchAdBannerParams): Promise<AdBann
|
|
|
264
284
|
|
|
265
285
|
/**
|
|
266
286
|
* Track an ad impression (when ad is viewed)
|
|
267
|
-
*
|
|
287
|
+
* Sends tracking directly to TheAd Vantage API (or skips in dev mode)
|
|
268
288
|
*/
|
|
269
289
|
export async function trackImpression(adId: string): Promise<void> {
|
|
270
290
|
try {
|
|
271
|
-
|
|
291
|
+
// Get the API base URL to determine where to send tracking
|
|
292
|
+
const apiBaseUrl = getApiBaseUrl();
|
|
293
|
+
|
|
294
|
+
// Build tracking URL - remove /api/ads if present, then append /api/ads/track
|
|
295
|
+
let trackingUrl = apiBaseUrl.trim();
|
|
296
|
+
if (trackingUrl.endsWith('/api/ads')) {
|
|
297
|
+
trackingUrl = trackingUrl.replace('/api/ads', '/api/ads/track');
|
|
298
|
+
} else {
|
|
299
|
+
// Remove trailing slash if present
|
|
300
|
+
trackingUrl = trackingUrl.replace(/\/$/, '') + '/api/ads/track';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// Check if we should skip tracking (dev mode)
|
|
304
|
+
const useDevFlags = shouldUseDevFlags();
|
|
305
|
+
if (useDevFlags) {
|
|
306
|
+
console.log(`[DEV] Impression tracking skipped for ad: ${adId}`);
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const response = await fetch(trackingUrl, {
|
|
272
311
|
method: 'POST',
|
|
273
312
|
headers: {
|
|
274
313
|
'Content-Type': 'application/json',
|
|
314
|
+
'Accept': 'application/json',
|
|
275
315
|
},
|
|
276
316
|
body: JSON.stringify({
|
|
277
317
|
action: 'impression',
|
|
@@ -279,6 +319,11 @@ export async function trackImpression(adId: string): Promise<void> {
|
|
|
279
319
|
}),
|
|
280
320
|
});
|
|
281
321
|
|
|
322
|
+
if (!response.ok) {
|
|
323
|
+
console.warn(`[AdBanner] Tracking impression failed: ${response.status} ${response.statusText}`);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
282
327
|
const data = await response.json();
|
|
283
328
|
if (data.dev_mode) {
|
|
284
329
|
console.log(`[DEV] Impression tracking skipped for ad: ${adId}`);
|
|
@@ -291,14 +336,34 @@ export async function trackImpression(adId: string): Promise<void> {
|
|
|
291
336
|
|
|
292
337
|
/**
|
|
293
338
|
* Track an ad click
|
|
294
|
-
*
|
|
339
|
+
* Sends tracking directly to TheAd Vantage API (or skips in dev mode)
|
|
295
340
|
*/
|
|
296
341
|
export async function trackClick(adId: string): Promise<void> {
|
|
297
342
|
try {
|
|
298
|
-
|
|
343
|
+
// Get the API base URL to determine where to send tracking
|
|
344
|
+
const apiBaseUrl = getApiBaseUrl();
|
|
345
|
+
|
|
346
|
+
// Build tracking URL - remove /api/ads if present, then append /api/ads/track
|
|
347
|
+
let trackingUrl = apiBaseUrl.trim();
|
|
348
|
+
if (trackingUrl.endsWith('/api/ads')) {
|
|
349
|
+
trackingUrl = trackingUrl.replace('/api/ads', '/api/ads/track');
|
|
350
|
+
} else {
|
|
351
|
+
// Remove trailing slash if present
|
|
352
|
+
trackingUrl = trackingUrl.replace(/\/$/, '') + '/api/ads/track';
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Check if we should skip tracking (dev mode)
|
|
356
|
+
const useDevFlags = shouldUseDevFlags();
|
|
357
|
+
if (useDevFlags) {
|
|
358
|
+
console.log(`[DEV] Click tracking skipped for ad: ${adId}`);
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
const response = await fetch(trackingUrl, {
|
|
299
363
|
method: 'POST',
|
|
300
364
|
headers: {
|
|
301
365
|
'Content-Type': 'application/json',
|
|
366
|
+
'Accept': 'application/json',
|
|
302
367
|
},
|
|
303
368
|
body: JSON.stringify({
|
|
304
369
|
action: 'click',
|
|
@@ -306,6 +371,11 @@ export async function trackClick(adId: string): Promise<void> {
|
|
|
306
371
|
}),
|
|
307
372
|
});
|
|
308
373
|
|
|
374
|
+
if (!response.ok) {
|
|
375
|
+
console.warn(`[AdBanner] Tracking click failed: ${response.status} ${response.statusText}`);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
|
|
309
379
|
const data = await response.json();
|
|
310
380
|
if (data.dev_mode) {
|
|
311
381
|
console.log(`[DEV] Click tracking skipped for ad: ${adId}`);
|
|
@@ -31,47 +31,78 @@ function isLocalhost(): boolean {
|
|
|
31
31
|
* 5. Production mode (default) → https://thead-vantage.com (full tracking)
|
|
32
32
|
*/
|
|
33
33
|
export function getApiBaseUrl(explicitApiUrl?: string): string {
|
|
34
|
+
let selectedUrl: string | undefined;
|
|
35
|
+
let reason: string | undefined;
|
|
36
|
+
|
|
34
37
|
// Priority 1: Explicit API URL override (highest priority)
|
|
35
38
|
if (explicitApiUrl) {
|
|
36
|
-
|
|
39
|
+
selectedUrl = explicitApiUrl;
|
|
40
|
+
reason = 'explicit API URL parameter';
|
|
37
41
|
}
|
|
38
|
-
|
|
39
42
|
// Priority 2: Environment variable for custom API URL
|
|
40
43
|
// This allows platform developers to point to their own platform
|
|
41
|
-
if (typeof window !== 'undefined') {
|
|
44
|
+
else if (typeof window !== 'undefined') {
|
|
42
45
|
// Check runtime config first (for browser contexts)
|
|
43
46
|
const runtimeUrl = (window as any).__THEAD_VANTAGE_API_URL__;
|
|
44
|
-
if (runtimeUrl) {
|
|
45
|
-
|
|
47
|
+
if (runtimeUrl && typeof runtimeUrl === 'string' && runtimeUrl.trim()) {
|
|
48
|
+
// Validate that it's not pointing to the current page's origin (would be wrong)
|
|
49
|
+
const currentOrigin = window.location.origin;
|
|
50
|
+
if (!runtimeUrl.startsWith(currentOrigin)) {
|
|
51
|
+
selectedUrl = runtimeUrl;
|
|
52
|
+
reason = 'runtime config (window.__THEAD_VANTAGE_API_URL__)';
|
|
53
|
+
} else {
|
|
54
|
+
console.warn('[TheAd Vantage] Runtime API URL points to current origin, ignoring:', runtimeUrl);
|
|
55
|
+
}
|
|
46
56
|
}
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
// Check environment variable (works in both server and client)
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
60
|
+
if (!selectedUrl) {
|
|
61
|
+
const customUrl = process.env.NEXT_PUBLIC_THEAD_VANTAGE_API_URL;
|
|
62
|
+
if (customUrl && customUrl.trim()) {
|
|
63
|
+
// Validate that it's not pointing to localhost without being TheAd Vantage dev mode
|
|
64
|
+
if (customUrl.includes('localhost') && !customUrl.includes('localhost:3001')) {
|
|
65
|
+
console.warn('[TheAd Vantage] Custom API URL points to localhost (not :3001), this may be incorrect:', customUrl);
|
|
66
|
+
}
|
|
67
|
+
selectedUrl = customUrl;
|
|
68
|
+
reason = 'NEXT_PUBLIC_THEAD_VANTAGE_API_URL environment variable';
|
|
69
|
+
}
|
|
53
70
|
}
|
|
54
71
|
|
|
55
72
|
// Priority 3: TheAd Vantage dev mode
|
|
56
73
|
// Only for TheAd Vantage developers testing locally with localhost:3001
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
74
|
+
if (!selectedUrl) {
|
|
75
|
+
const isTheadVantageDevMode = typeof window !== 'undefined'
|
|
76
|
+
? (window as any).__THEAD_VANTAGE_DEV_MODE__ === true ||
|
|
77
|
+
process.env.NEXT_PUBLIC_THEAD_VANTAGE_DEV_MODE === 'true'
|
|
78
|
+
: process.env.NEXT_PUBLIC_THEAD_VANTAGE_DEV_MODE === 'true';
|
|
79
|
+
|
|
80
|
+
if (isTheadVantageDevMode) {
|
|
81
|
+
selectedUrl = 'http://localhost:3001/api/ads';
|
|
82
|
+
reason = 'TheAd Vantage dev mode (NEXT_PUBLIC_THEAD_VANTAGE_DEV_MODE=true)';
|
|
83
|
+
}
|
|
64
84
|
}
|
|
65
85
|
|
|
66
86
|
// Priority 4: Localhost development (platform developers on localhost)
|
|
67
87
|
// Use production API but will add dev flags to prevent tracking
|
|
68
|
-
if (isLocalhost()) {
|
|
69
|
-
|
|
88
|
+
if (!selectedUrl && isLocalhost()) {
|
|
89
|
+
selectedUrl = 'https://thead-vantage.com/api/ads';
|
|
90
|
+
reason = 'localhost detection (production API with dev flags)';
|
|
70
91
|
}
|
|
71
92
|
|
|
72
93
|
// Priority 5: Production mode (default)
|
|
73
94
|
// Platform developers use this by default in production
|
|
74
|
-
|
|
95
|
+
if (!selectedUrl) {
|
|
96
|
+
selectedUrl = 'https://thead-vantage.com/api/ads';
|
|
97
|
+
reason = 'production mode (default)';
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Log which URL was selected (helpful for debugging)
|
|
101
|
+
if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
|
|
102
|
+
console.log(`[TheAd Vantage] API Base URL selected: ${selectedUrl} (reason: ${reason})`);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return selectedUrl;
|
|
75
106
|
}
|
|
76
107
|
|
|
77
108
|
/**
|