shopkit-analytics 1.1.2 → 1.2.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/dist/adapters/index.d.mts +1 -1
- package/dist/adapters/index.d.ts +1 -1
- package/dist/adapters/index.js +9 -372
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +2 -6
- package/dist/affiliate/index.d.mts +2 -2
- package/dist/affiliate/index.d.ts +2 -2
- package/dist/{affiliate-tracker-B9nV3E9y.d.mts → affiliate-tracker-BgHwibPv.d.mts} +1 -1
- package/dist/{affiliate-tracker-B9nV3E9y.d.ts → affiliate-tracker-BgHwibPv.d.ts} +1 -1
- package/dist/{chunk-FXJKI3KV.mjs → chunk-CYSLR6EI.mjs} +1 -14
- package/dist/chunk-CYSLR6EI.mjs.map +1 -0
- package/dist/{chunk-EBZU3HYF.mjs → chunk-YJE5NOFF.mjs} +11 -372
- package/dist/chunk-YJE5NOFF.mjs.map +1 -0
- package/dist/{chunk-ANVCP4FV.mjs → chunk-ZTIVTB5J.mjs} +2 -2
- package/dist/events/index.d.mts +1 -1
- package/dist/events/index.d.ts +1 -1
- package/dist/events/index.js +9 -27
- package/dist/events/index.js.map +1 -1
- package/dist/events/index.mjs +3 -3
- package/dist/{index-C3YOamdo.d.ts → index-B-TnPt4F.d.ts} +1 -89
- package/dist/{index-BgLdfy-M.d.mts → index-fYvOG_to.d.mts} +1 -89
- package/dist/index.d.mts +6 -127
- package/dist/index.d.ts +6 -127
- package/dist/index.js +9 -718
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3 -337
- package/dist/index.mjs.map +1 -1
- package/dist/types.d.mts +2 -129
- package/dist/types.d.ts +2 -129
- package/dist/types.js +0 -13
- package/dist/types.js.map +1 -1
- package/dist/types.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-EBZU3HYF.mjs.map +0 -1
- package/dist/chunk-FXJKI3KV.mjs.map +0 -1
- /package/dist/{chunk-ANVCP4FV.mjs.map → chunk-ZTIVTB5J.mjs.map} +0 -0
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
initTracking,
|
|
3
3
|
initializeEventTracking
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ZTIVTB5J.mjs";
|
|
5
5
|
import {
|
|
6
6
|
AffiliateTracker_default,
|
|
7
7
|
captureAffiliateParams,
|
|
@@ -16,8 +16,6 @@ import {
|
|
|
16
16
|
import {
|
|
17
17
|
BaseAdapter,
|
|
18
18
|
GoogleAdapter,
|
|
19
|
-
KwikCheckoutAdapter,
|
|
20
|
-
KwikPassAdapter,
|
|
21
19
|
MoengageAdapter,
|
|
22
20
|
MultiPixelAdapter,
|
|
23
21
|
PostHogAdapter,
|
|
@@ -25,7 +23,7 @@ import {
|
|
|
25
23
|
createAdapterLogger,
|
|
26
24
|
createLogger,
|
|
27
25
|
logger
|
|
28
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-YJE5NOFF.mjs";
|
|
29
27
|
import {
|
|
30
28
|
PRIMA_EXPERIMENT_COOKIES,
|
|
31
29
|
PRIMA_EXPERIMENT_COOKIE_NAMES,
|
|
@@ -38,7 +36,7 @@ import {
|
|
|
38
36
|
} from "./chunk-UFDN3A6M.mjs";
|
|
39
37
|
import {
|
|
40
38
|
EventType
|
|
41
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-CYSLR6EI.mjs";
|
|
42
40
|
import {
|
|
43
41
|
eventPublisher,
|
|
44
42
|
eventSubscriber,
|
|
@@ -170,20 +168,6 @@ var ShopkitAnalytics = ({
|
|
|
170
168
|
adapters.push(shopifyAdapter);
|
|
171
169
|
logger2.info("Added Shopify Analytics adapter");
|
|
172
170
|
}
|
|
173
|
-
if (config.kwikpass) {
|
|
174
|
-
const kwikpassAdapter = new KwikPassAdapter(
|
|
175
|
-
addLoggerConfig(config.kwikpass)
|
|
176
|
-
);
|
|
177
|
-
adapters.push(kwikpassAdapter);
|
|
178
|
-
logger2.info("Added KwikPass Analytics adapter");
|
|
179
|
-
}
|
|
180
|
-
if (config.kwikcheckout) {
|
|
181
|
-
const kwikcheckoutAdapter = new KwikCheckoutAdapter(
|
|
182
|
-
addLoggerConfig(config.kwikcheckout)
|
|
183
|
-
);
|
|
184
|
-
adapters.push(kwikcheckoutAdapter);
|
|
185
|
-
logger2.info("Added KwikCheckout Analytics adapter");
|
|
186
|
-
}
|
|
187
171
|
if (config.customAdapters) {
|
|
188
172
|
adapters.push(...config.customAdapters);
|
|
189
173
|
logger2.info(`Added ${config.customAdapters.length} custom adapters`);
|
|
@@ -219,333 +203,16 @@ var ShopkitAnalytics = ({
|
|
|
219
203
|
] });
|
|
220
204
|
};
|
|
221
205
|
var ShopkitAnalytics_default = ShopkitAnalytics;
|
|
222
|
-
|
|
223
|
-
// src/utils/pii-hashing.ts
|
|
224
|
-
async function hashString(value, normalize) {
|
|
225
|
-
if (!value) {
|
|
226
|
-
return void 0;
|
|
227
|
-
}
|
|
228
|
-
try {
|
|
229
|
-
const normalizedValue = normalize ? normalize(value) : value;
|
|
230
|
-
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
231
|
-
const encoder = new TextEncoder();
|
|
232
|
-
const data = encoder.encode(normalizedValue);
|
|
233
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
234
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
235
|
-
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
236
|
-
return hashHex;
|
|
237
|
-
}
|
|
238
|
-
} catch (error) {
|
|
239
|
-
console.error("Error hashing with SHA-256:", error);
|
|
240
|
-
}
|
|
241
|
-
return fallbackHash(value);
|
|
242
|
-
}
|
|
243
|
-
async function hashIP(ip) {
|
|
244
|
-
return hashString(ip, (value) => value.trim());
|
|
245
|
-
}
|
|
246
|
-
function fallbackHash(value) {
|
|
247
|
-
let hash = 0;
|
|
248
|
-
if (value.length === 0) {
|
|
249
|
-
return "0";
|
|
250
|
-
}
|
|
251
|
-
for (let i = 0; i < value.length; i++) {
|
|
252
|
-
const char = value.charCodeAt(i);
|
|
253
|
-
hash = (hash << 5) - hash + char;
|
|
254
|
-
hash = hash & hash;
|
|
255
|
-
}
|
|
256
|
-
return Math.abs(hash).toString(16).padStart(16, "0");
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// src/services/facebook-capi.service.ts
|
|
260
|
-
var RateLimiter = class {
|
|
261
|
-
constructor(requestsPerMinute = 100) {
|
|
262
|
-
this.store = /* @__PURE__ */ new Map();
|
|
263
|
-
this.limit = requestsPerMinute;
|
|
264
|
-
this.window = 60 * 1e3;
|
|
265
|
-
}
|
|
266
|
-
check(ip) {
|
|
267
|
-
const now = Date.now();
|
|
268
|
-
let entry = this.store.get(ip);
|
|
269
|
-
if (entry && entry.resetTime < now) {
|
|
270
|
-
this.store.delete(ip);
|
|
271
|
-
entry = void 0;
|
|
272
|
-
}
|
|
273
|
-
if (!entry) {
|
|
274
|
-
entry = { count: 0, resetTime: now + this.window };
|
|
275
|
-
this.store.set(ip, entry);
|
|
276
|
-
}
|
|
277
|
-
const allowed = entry.count < this.limit;
|
|
278
|
-
entry.count++;
|
|
279
|
-
return {
|
|
280
|
-
allowed,
|
|
281
|
-
remaining: Math.max(0, this.limit - entry.count)
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
var FacebookCAPIService = class {
|
|
286
|
-
constructor() {
|
|
287
|
-
this.rateLimiter = new RateLimiter(100);
|
|
288
|
-
// 100 requests per minute per IP
|
|
289
|
-
this.maxRetries = 3;
|
|
290
|
-
this.baseDelay = 1e3;
|
|
291
|
-
// 1 second
|
|
292
|
-
this.timeout = 3e4;
|
|
293
|
-
// 30 seconds
|
|
294
|
-
this.debug = false;
|
|
295
|
-
}
|
|
296
|
-
// Override in consuming projects if needed
|
|
297
|
-
/**
|
|
298
|
-
* Check if request is within rate limits
|
|
299
|
-
*/
|
|
300
|
-
checkRateLimit(clientIp) {
|
|
301
|
-
return this.rateLimiter.check(clientIp);
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Generate a unique event ID for deduplication
|
|
305
|
-
*/
|
|
306
|
-
generateEventId(eventName) {
|
|
307
|
-
const timestamp = Date.now();
|
|
308
|
-
const randomString = Math.random().toString(36).substring(2, 8);
|
|
309
|
-
return `${timestamp}_${randomString}_${eventName}`;
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Extract fbc cookie from request headers
|
|
313
|
-
*/
|
|
314
|
-
extractFbcFromRequest(request) {
|
|
315
|
-
const cookieHeader = request.headers.get("cookie");
|
|
316
|
-
if (!cookieHeader) return void 0;
|
|
317
|
-
const cookies = cookieHeader.split(";");
|
|
318
|
-
for (const cookie of cookies) {
|
|
319
|
-
const [name, value] = cookie.trim().split("=");
|
|
320
|
-
if (name === "_fbc") {
|
|
321
|
-
return value;
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
return void 0;
|
|
325
|
-
}
|
|
326
|
-
/**
|
|
327
|
-
* Get client IP address from request headers
|
|
328
|
-
*/
|
|
329
|
-
getClientIpAddress(request) {
|
|
330
|
-
const forwarded = request.headers.get("x-forwarded-for");
|
|
331
|
-
const realIp = request.headers.get("x-real-ip");
|
|
332
|
-
const cfConnectingIp = request.headers.get("cf-connecting-ip");
|
|
333
|
-
return forwarded?.split(",")[0] || realIp || cfConnectingIp || void 0;
|
|
334
|
-
}
|
|
335
|
-
/**
|
|
336
|
-
* Convert event to Facebook CAPI format with optional PII hashing
|
|
337
|
-
* Note: This method is now async to support SHA-256 IP hashing
|
|
338
|
-
*/
|
|
339
|
-
async convertToCapiEvent(eventName, eventId, timestamp, enhancedParams, userInfo) {
|
|
340
|
-
const eventTime = timestamp ? Math.floor(timestamp / 1e3) : Math.floor(Date.now() / 1e3);
|
|
341
|
-
let hashedIp;
|
|
342
|
-
if (userInfo?.clientIpAddress) {
|
|
343
|
-
try {
|
|
344
|
-
hashedIp = await hashIP(userInfo.clientIpAddress);
|
|
345
|
-
} catch (error) {
|
|
346
|
-
console.error("[Facebook CAPI] Error hashing IP:", error);
|
|
347
|
-
hashedIp = userInfo.clientIpAddress;
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
if (this.debug) {
|
|
351
|
-
console.log("[Facebook CAPI] Event Conversion", {
|
|
352
|
-
eventName,
|
|
353
|
-
eventId,
|
|
354
|
-
eventTime,
|
|
355
|
-
timestamp_input: timestamp,
|
|
356
|
-
fbp: !!userInfo?.fbp,
|
|
357
|
-
fbc: !!userInfo?.fbc,
|
|
358
|
-
ip_hashed: !!hashedIp,
|
|
359
|
-
pii_fields: {
|
|
360
|
-
em: !!userInfo?.em,
|
|
361
|
-
ph: !!userInfo?.ph,
|
|
362
|
-
fn: !!userInfo?.fn,
|
|
363
|
-
ln: !!userInfo?.ln
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
}
|
|
367
|
-
const capiEvent = {
|
|
368
|
-
event_name: eventName,
|
|
369
|
-
event_time: eventTime,
|
|
370
|
-
event_id: eventId,
|
|
371
|
-
action_source: "website",
|
|
372
|
-
user_data: {
|
|
373
|
-
client_ip_address: hashedIp,
|
|
374
|
-
client_user_agent: userInfo?.clientUserAgent,
|
|
375
|
-
fbc: userInfo?.fbc,
|
|
376
|
-
fbp: userInfo?.fbp
|
|
377
|
-
},
|
|
378
|
-
custom_data: enhancedParams
|
|
379
|
-
};
|
|
380
|
-
if (userInfo?.em) {
|
|
381
|
-
capiEvent.user_data.em = userInfo.em;
|
|
382
|
-
}
|
|
383
|
-
if (userInfo?.ph) {
|
|
384
|
-
capiEvent.user_data.ph = userInfo.ph;
|
|
385
|
-
}
|
|
386
|
-
if (userInfo?.fn) {
|
|
387
|
-
capiEvent.user_data.fn = userInfo.fn;
|
|
388
|
-
}
|
|
389
|
-
if (userInfo?.ln) {
|
|
390
|
-
capiEvent.user_data.ln = userInfo.ln;
|
|
391
|
-
}
|
|
392
|
-
if (userInfo?.country) {
|
|
393
|
-
capiEvent.user_data.country = userInfo.country;
|
|
394
|
-
}
|
|
395
|
-
if (userInfo?.st) {
|
|
396
|
-
capiEvent.user_data.st = userInfo.st;
|
|
397
|
-
}
|
|
398
|
-
if (userInfo?.zp) {
|
|
399
|
-
capiEvent.user_data.zp = userInfo.zp;
|
|
400
|
-
}
|
|
401
|
-
if (userInfo?.db) {
|
|
402
|
-
capiEvent.user_data.db = userInfo.db;
|
|
403
|
-
}
|
|
404
|
-
if (userInfo?.external_id) {
|
|
405
|
-
capiEvent.user_data.external_id = userInfo.external_id;
|
|
406
|
-
}
|
|
407
|
-
return capiEvent;
|
|
408
|
-
}
|
|
409
|
-
/**
|
|
410
|
-
* Send event to Facebook CAPI with retry logic
|
|
411
|
-
*/
|
|
412
|
-
async sendToFacebook(config, capiEvent) {
|
|
413
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
414
|
-
try {
|
|
415
|
-
const payload = {
|
|
416
|
-
data: [capiEvent],
|
|
417
|
-
test_event_code: config.testEventCode
|
|
418
|
-
};
|
|
419
|
-
if (this.debug) {
|
|
420
|
-
console.log("[Facebook CAPI] Sending to Facebook", {
|
|
421
|
-
pixelId: config.pixelId,
|
|
422
|
-
eventId: capiEvent.event_id,
|
|
423
|
-
eventName: capiEvent.event_name,
|
|
424
|
-
attempt: attempt + 1
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
const url = `${config.baseUrl}/${config.pixelId}/events?access_token=${config.accessToken}`;
|
|
428
|
-
const controller = new AbortController();
|
|
429
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
430
|
-
try {
|
|
431
|
-
const response = await fetch(url, {
|
|
432
|
-
method: "POST",
|
|
433
|
-
headers: {
|
|
434
|
-
"Content-Type": "application/json"
|
|
435
|
-
},
|
|
436
|
-
body: JSON.stringify(payload),
|
|
437
|
-
signal: controller.signal
|
|
438
|
-
});
|
|
439
|
-
clearTimeout(timeoutId);
|
|
440
|
-
if (!response.ok) {
|
|
441
|
-
if ((response.status >= 500 || response.status === 429) && attempt < this.maxRetries) {
|
|
442
|
-
const delay = this.baseDelay * Math.pow(2, attempt);
|
|
443
|
-
if (this.debug) {
|
|
444
|
-
console.log(`[Facebook CAPI] Retrying in ${delay}ms`, {
|
|
445
|
-
pixelId: config.pixelId,
|
|
446
|
-
status: response.status
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
console.error(
|
|
453
|
-
`Facebook CAPI Error for pixel ${config.pixelId} (attempt ${attempt + 1}/${this.maxRetries + 1}):`,
|
|
454
|
-
{ status: response.status }
|
|
455
|
-
);
|
|
456
|
-
return {
|
|
457
|
-
success: false,
|
|
458
|
-
error: `Facebook CAPI request failed: ${response.status}`,
|
|
459
|
-
pixelId: config.pixelId
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
const result = await response.json();
|
|
463
|
-
if (this.debug) {
|
|
464
|
-
console.log(
|
|
465
|
-
`Facebook CAPI: Events sent successfully to pixel ${config.pixelId}`,
|
|
466
|
-
{
|
|
467
|
-
events_received: result.events_received
|
|
468
|
-
}
|
|
469
|
-
);
|
|
470
|
-
}
|
|
471
|
-
return {
|
|
472
|
-
success: true,
|
|
473
|
-
data: result,
|
|
474
|
-
pixelId: config.pixelId
|
|
475
|
-
};
|
|
476
|
-
} finally {
|
|
477
|
-
clearTimeout(timeoutId);
|
|
478
|
-
}
|
|
479
|
-
} catch (error) {
|
|
480
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
481
|
-
if (attempt < this.maxRetries) {
|
|
482
|
-
const delay = this.baseDelay * Math.pow(2, attempt);
|
|
483
|
-
if (this.debug) {
|
|
484
|
-
console.log(`[Facebook CAPI] Retrying in ${delay}ms after error`, {
|
|
485
|
-
pixelId: config.pixelId,
|
|
486
|
-
error: errorMessage
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
console.error(
|
|
493
|
-
`Facebook CAPI Network Error for pixel ${config.pixelId} (attempt ${attempt + 1}/${this.maxRetries + 1}):`,
|
|
494
|
-
{ error: errorMessage }
|
|
495
|
-
);
|
|
496
|
-
return {
|
|
497
|
-
success: false,
|
|
498
|
-
error: `Network error: ${errorMessage}`,
|
|
499
|
-
pixelId: config.pixelId
|
|
500
|
-
};
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
return {
|
|
504
|
-
success: false,
|
|
505
|
-
error: "Failed after all retry attempts",
|
|
506
|
-
pixelId: config.pixelId
|
|
507
|
-
};
|
|
508
|
-
}
|
|
509
|
-
/**
|
|
510
|
-
* Validate request body
|
|
511
|
-
*/
|
|
512
|
-
validateRequestBody(eventName, enhancedParams, bodySize) {
|
|
513
|
-
if (!eventName || !enhancedParams) {
|
|
514
|
-
return {
|
|
515
|
-
valid: false,
|
|
516
|
-
error: "eventName and enhancedParams are required"
|
|
517
|
-
};
|
|
518
|
-
}
|
|
519
|
-
if (bodySize > 1024 * 1024) {
|
|
520
|
-
return {
|
|
521
|
-
valid: false,
|
|
522
|
-
error: "Request body too large (max 1MB)"
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
if (!/^[a-zA-Z0-9_]+$/.test(eventName)) {
|
|
526
|
-
return {
|
|
527
|
-
valid: false,
|
|
528
|
-
error: "eventName must contain only alphanumeric characters and underscores"
|
|
529
|
-
};
|
|
530
|
-
}
|
|
531
|
-
return { valid: true };
|
|
532
|
-
}
|
|
533
|
-
};
|
|
534
|
-
var facebookCAPIService = new FacebookCAPIService();
|
|
535
206
|
export {
|
|
536
207
|
AffiliateTracker_default as AffiliateTracker,
|
|
537
208
|
BaseAdapter,
|
|
538
209
|
EventType,
|
|
539
|
-
FacebookCAPIService,
|
|
540
210
|
GoogleAdapter,
|
|
541
|
-
KwikCheckoutAdapter,
|
|
542
|
-
KwikPassAdapter,
|
|
543
211
|
MoengageAdapter,
|
|
544
212
|
MultiPixelAdapter,
|
|
545
213
|
PRIMA_EXPERIMENT_COOKIES,
|
|
546
214
|
PRIMA_EXPERIMENT_COOKIE_NAMES,
|
|
547
215
|
PostHogAdapter,
|
|
548
|
-
RateLimiter,
|
|
549
216
|
ShopifyAdapter,
|
|
550
217
|
ShopifyAnalyticsScript,
|
|
551
218
|
ShopkitAnalytics_default as ShopkitAnalytics,
|
|
@@ -556,7 +223,6 @@ export {
|
|
|
556
223
|
ShopkitAnalytics_default as default,
|
|
557
224
|
eventPublisher,
|
|
558
225
|
eventSubscriber,
|
|
559
|
-
facebookCAPIService,
|
|
560
226
|
generateEventId,
|
|
561
227
|
getAffiliateParams,
|
|
562
228
|
getAffiliateSource,
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ShopkitAnalytics.tsx","../src/shopify/ShopifyAnalyticsScript.tsx","../src/utils/pii-hashing.ts","../src/services/facebook-capi.service.ts"],"sourcesContent":["\"use client\";\n\n/**\n * ShopkitAnalytics Component\n *\n * Main analytics provider component that initializes and manages\n * all configured analytics adapters.\n *\n * @module @shopkit/analytics\n */\n\nimport React, { useEffect } from \"react\";\nimport { initializeEventTracking } from \"./events/init\";\nimport { AffiliateTracker } from \"./affiliate\";\nimport { createLogger, type LoggerConfig } from \"./logger\";\nimport {\n GoogleAdapter,\n MultiPixelAdapter,\n MoengageAdapter,\n PostHogAdapter,\n ShopifyAdapter,\n KwikPassAdapter,\n KwikCheckoutAdapter,\n} from \"./adapters\";\nimport type {\n GoogleAdapterConfig,\n MultiPixelAdapterConfig,\n MoengageAdapterConfig,\n PostHogAdapterConfig,\n ShopifyAdapterConfig,\n KwikPassConfig,\n KwikCheckoutConfig,\n} from \"./adapters\";\nimport type { TrackingAdapter } from \"./events/subscriber\";\nimport type { AffiliateConfig } from \"./affiliate/types\";\nimport { ShopifyAnalyticsScript } from \"./shopify/ShopifyAnalyticsScript\";\n\n/**\n * Configuration for ShopkitAnalytics component\n *\n * @example\n * ```typescript\n * const config: ShopkitAnalyticsConfig = {\n * googleAnalytics: { measurementId: 'G-XXXXXX' },\n * facebookPixel: { pixelId: '123456789' },\n * posthog: { apiKey: 'phc_xxx', apiHost: 'https://app.posthog.com' },\n * logger: { enabled: true, level: 'info' },\n * };\n * ```\n */\nexport interface ShopkitAnalyticsConfig {\n // Analytics Adapters\n googleAnalytics?: GoogleAdapterConfig;\n facebookMultiPixel?: MultiPixelAdapterConfig;\n moengage?: MoengageAdapterConfig;\n posthog?: PostHogAdapterConfig;\n shopify?: ShopifyAdapterConfig;\n kwikpass?: KwikPassConfig;\n kwikcheckout?: KwikCheckoutConfig;\n customAdapters?: TrackingAdapter[];\n\n // Affiliate Tracking\n affiliate?: {\n enabled?: boolean;\n config?: Partial<AffiliateConfig>;\n autoCapture?: boolean;\n };\n\n // Logger Settings\n logger?: LoggerConfig;\n\n // General Settings (deprecated - use logger.enabled instead)\n enableDebugLogs?: boolean;\n autoInitialize?: boolean;\n}\n\n/**\n * Props for ShopkitAnalytics component\n */\nexport interface ShopkitAnalyticsProps {\n /** Analytics configuration object */\n config: ShopkitAnalyticsConfig;\n /** Optional children to render */\n children?: React.ReactNode;\n /** Callback when analytics initialization completes */\n onInitialized?: () => void;\n /** Callback when an error occurs during initialization */\n onError?: (error: Error) => void;\n}\n\n/**\n * ShopkitAnalytics Component\n *\n * The main analytics provider that initializes all configured tracking adapters\n * and sets up event publishing infrastructure.\n *\n * Place this component in your app's root layout to enable analytics tracking\n * throughout your application.\n *\n * @param props - Component props\n * @returns React component with analytics initialization and affiliate tracking\n *\n * @example\n * ```typescript\n * // In your root layout\n * import { ShopkitAnalytics } from '@shopkit/analytics';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <ShopkitAnalytics\n * config={{\n * googleAnalytics: { measurementId: 'G-XXXXXX' },\n * facebookPixel: { pixelId: '123456789' },\n * }}\n * onInitialized={() => console.log('Analytics ready')}\n * onError={(err) => console.error('Analytics error:', err)}\n * >\n * {children}\n * </ShopkitAnalytics>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nconst ShopkitAnalytics: React.FC<ShopkitAnalyticsProps> = ({\n config,\n children,\n onInitialized,\n onError,\n}) => {\n useEffect(() => {\n const initializeAnalytics = async () => {\n if (config.autoInitialize === false) return;\n\n // Create logger instance\n const loggerConfig: LoggerConfig = {\n enabled: config.logger?.enabled ?? config.enableDebugLogs ?? true,\n level: config.logger?.level ?? \"info\",\n name: \"@shopkit/analytics\",\n prettyPrint: config.logger?.prettyPrint ?? false,\n };\n\n const logger = createLogger(loggerConfig);\n\n try {\n logger.info(\"Starting initialization...\");\n\n const adapters: TrackingAdapter[] = [];\n\n // Helper function to add logger config to adapter config\n const addLoggerConfig = (adapterConfig: any) => ({\n ...adapterConfig,\n enableDebugLogs: loggerConfig.enabled,\n logLevel: loggerConfig.level,\n });\n\n // Google Analytics 4\n if (config.googleAnalytics) {\n const gaAdapter = new GoogleAdapter(\n addLoggerConfig(config.googleAnalytics),\n );\n adapters.push(gaAdapter);\n logger.info(\"Added Google Analytics adapter\");\n }\n\n // Facebook Multi-Pixel\n if (config.facebookMultiPixel) {\n const multiPixelAdapter = new MultiPixelAdapter(\n addLoggerConfig(config.facebookMultiPixel),\n );\n adapters.push(multiPixelAdapter);\n logger.info(\n `Added Facebook Multi-Pixel adapter with ${config.facebookMultiPixel.pixels.length} pixels`,\n );\n }\n\n // MoEngage\n if (config.moengage) {\n const moengageAdapter = new MoengageAdapter(\n addLoggerConfig(config.moengage),\n );\n adapters.push(moengageAdapter);\n logger.info(\"Added MoEngage adapter\");\n }\n\n // PostHog\n if (config.posthog) {\n const posthogAdapter = new PostHogAdapter(\n addLoggerConfig(config.posthog),\n );\n adapters.push(posthogAdapter);\n logger.info(\"Added PostHog adapter\");\n }\n\n // Shopify Analytics\n if (config.shopify) {\n const shopifyAdapter = new ShopifyAdapter(\n addLoggerConfig(config.shopify),\n );\n adapters.push(shopifyAdapter);\n logger.info(\"Added Shopify Analytics adapter\");\n }\n\n // KwikPass Analytics\n if (config.kwikpass) {\n const kwikpassAdapter = new KwikPassAdapter(\n addLoggerConfig(config.kwikpass),\n );\n adapters.push(kwikpassAdapter);\n logger.info(\"Added KwikPass Analytics adapter\");\n }\n\n // KwikCheckout Analytics\n if (config.kwikcheckout) {\n const kwikcheckoutAdapter = new KwikCheckoutAdapter(\n addLoggerConfig(config.kwikcheckout),\n );\n adapters.push(kwikcheckoutAdapter);\n logger.info(\"Added KwikCheckout Analytics adapter\");\n }\n\n // Custom adapters\n if (config.customAdapters) {\n adapters.push(...config.customAdapters);\n logger.info(`Added ${config.customAdapters.length} custom adapters`);\n }\n\n if (adapters.length === 0) {\n logger.warn(\"No analytics adapters configured\");\n } else {\n // Initialize the event tracking system\n await initializeEventTracking(adapters);\n logger.info(\"Event tracking initialized with adapters\", {\n adapters: adapters.map((a) => a.name),\n });\n }\n\n logger.info(\"Initialization completed successfully\");\n onInitialized?.();\n } catch (err) {\n const error =\n err instanceof Error\n ? err\n : new Error(\"Unknown initialization error\");\n logger.error(\"Initialization failed\", error);\n onError?.(error);\n }\n };\n\n initializeAnalytics();\n }, [config, onInitialized, onError]);\n\n return (\n <>\n {/* Affiliate Tracker */}\n {config.affiliate?.enabled !== false && (\n <AffiliateTracker\n config={config.affiliate?.config}\n autoCapture={config.affiliate?.autoCapture}\n />\n )}\n\n {/* Shopify Analytics Script - Only render if Shopify config is provided */}\n {config.shopify?.domain && (\n <ShopifyAnalyticsScript domain={config.shopify.domain} />\n )}\n\n {children ? <>{children}</> : null}\n </>\n );\n};\n\nexport default ShopkitAnalytics;\n","\"use client\";\n\nimport { useShopifyCookies } from \"@shopify/hydrogen-react\";\nimport { useEffect, useState } from \"react\";\nimport { EventType, publishEvent } from \"../events\";\n\n// Safe pathname hook that works in different environments\nfunction usePathnameCompat() {\n const [pathname, setPathname] = useState<string>(\"\");\n\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n setPathname(window.location.pathname);\n \n // Listen for navigation changes\n const handleLocationChange = () => {\n setPathname(window.location.pathname);\n };\n\n // Listen for popstate (back/forward navigation)\n window.addEventListener(\"popstate\", handleLocationChange);\n \n // For SPA navigation, we might need to listen to custom events\n // This is a fallback for when Next.js router is not available\n \n return () => {\n window.removeEventListener(\"popstate\", handleLocationChange);\n };\n }\n }, []);\n\n return pathname;\n}\n\n/**\n * Component to initialize Shopify Analytics cookies\n * This component handles the useShopifyCookies hook which must be used in a React component\n */\n\ninterface ShopifyAnalyticsScriptProps {\n domain: string;\n}\n\nexport function ShopifyAnalyticsScript({\n domain,\n}: ShopifyAnalyticsScriptProps) {\n const pathname = usePathnameCompat();\n\n const [sessionValid, setSessionValid] = useState(false);\n\n useShopifyCookies({\n hasUserConsent: true,\n domain: domain,\n });\n\n useEffect(() => {\n // Validate session after cookie initialization\n const validateSession = () => {\n const shopifyY = document.cookie.includes(\"_shopify_y=\");\n const shopifyS = document.cookie.includes(\"_shopify_s=\");\n\n if (shopifyY && shopifyS) {\n setSessionValid(true);\n console.log(\"✅ Shopify Analytics session validated\");\n } else {\n console.warn(\"⚠️ Shopify session cookies missing\");\n }\n };\n\n // Allow time for cookies to be set\n const timer = setTimeout(validateSession, 1000);\n return () => clearTimeout(timer);\n }, []);\n\n // Expose session status for debugging\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n (window as any).__shopifySessionValid = sessionValid;\n }\n }, [sessionValid]);\n\n useEffect(() => {\n const trackPageView = () => {\n publishEvent({\n type: EventType.SHOPIFY_PAGE_VIEW,\n });\n };\n\n const timer = setTimeout(trackPageView, 1500);\n return () => clearTimeout(timer);\n }, [pathname]);\n\n return null; // This component doesn't render anything\n}\n","/**\n * PII Hashing Utilities for Facebook CAPI\n *\n * Provides SHA-256 hashing functions for Personally Identifiable Information\n * before sending to Facebook's Conversion API. All functions normalize input\n * according to Facebook's guidelines.\n *\n * Reference: https://developers.facebook.com/docs/marketing-api/conversions-api/parameters\n */\n\n/**\n * Generic hash function for strings using SHA-256\n * @param value String to hash\n * @param normalize Optional normalization function to apply before hashing\n * @returns Promise resolving to SHA-256 hash or fallback hash\n */\nexport async function hashString(\n value: string | undefined | null,\n normalize?: (v: string) => string\n): Promise<string | undefined> {\n if (!value) {\n return undefined;\n }\n\n try {\n const normalizedValue = normalize ? normalize(value) : value;\n\n // Use Web Crypto API if available (modern browsers and Node.js)\n if (typeof crypto !== \"undefined\" && crypto.subtle) {\n const encoder = new TextEncoder();\n const data = encoder.encode(normalizedValue);\n const hashBuffer = await crypto.subtle.digest(\"SHA-256\", data);\n const hashArray = Array.from(new Uint8Array(hashBuffer));\n const hashHex = hashArray\n .map((b) => b.toString(16).padStart(2, \"0\"))\n .join(\"\");\n return hashHex;\n }\n } catch (error) {\n console.error(\"Error hashing with SHA-256:\", error);\n }\n\n // Fallback to simple hash if Web Crypto not available\n return fallbackHash(value);\n}\n\n/**\n * Hash email address\n * Normalized: trimmed and lowercase\n * @param email Email address to hash\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashEmail(\n email: string | undefined | null\n): Promise<string | undefined> {\n return hashString(email, (value) =>\n value.trim().toLowerCase().replace(/\\s+/g, \"\")\n );\n}\n\n/**\n * Hash phone number\n * Normalized: trimmed, lowercase, only digits\n * @param phone Phone number to hash\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashPhone(\n phone: string | undefined | null\n): Promise<string | undefined> {\n return hashString(phone, (value) =>\n value\n .trim()\n .toLowerCase()\n .replace(/\\D/g, \"\") // Remove all non-digits\n );\n}\n\n/**\n * Hash first name\n * Normalized: trimmed and lowercase\n * @param firstName First name to hash\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashFirstName(\n firstName: string | undefined | null\n): Promise<string | undefined> {\n return hashString(firstName, (value) =>\n value.trim().toLowerCase().replace(/\\s+/g, \"\")\n );\n}\n\n/**\n * Hash last name\n * Normalized: trimmed and lowercase\n * @param lastName Last name to hash\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashLastName(\n lastName: string | undefined | null\n): Promise<string | undefined> {\n return hashString(lastName, (value) =>\n value.trim().toLowerCase().replace(/\\s+/g, \"\")\n );\n}\n\n/**\n * Hash IP address\n * Normalized: trimmed\n * @param ip IP address to hash\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashIP(\n ip: string | undefined | null\n): Promise<string | undefined> {\n return hashString(ip, (value) => value.trim());\n}\n\n/**\n * Hash country code\n * Normalized: trimmed and uppercase\n * @param country Country code (US, CA, etc.)\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashCountry(\n country: string | undefined | null\n): Promise<string | undefined> {\n return hashString(country, (value) =>\n value.trim().toUpperCase().replace(/\\s+/g, \"\")\n );\n}\n\n/**\n * Hash state/province code\n * Normalized: trimmed and uppercase\n * @param state State/province code\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashState(\n state: string | undefined | null\n): Promise<string | undefined> {\n return hashString(state, (value) =>\n value.trim().toUpperCase().replace(/\\s+/g, \"\")\n );\n}\n\n/**\n * Hash zip/postal code\n * Normalized: trimmed and lowercase\n * @param zip Zip/postal code\n * @returns Promise resolving to SHA-256 hash\n */\nexport async function hashZip(\n zip: string | undefined | null\n): Promise<string | undefined> {\n return hashString(zip, (value) =>\n value.trim().toLowerCase().replace(/\\s+/g, \"\")\n );\n}\n\n/**\n * Utility function to hash multiple PII fields at once\n * @param pii Object containing PII fields\n * @returns Promise resolving to object with hashed fields\n */\nexport async function hashPIIFields(pii: {\n email?: string;\n phone?: string;\n firstName?: string;\n lastName?: string;\n country?: string;\n state?: string;\n zip?: string;\n dateOfBirth?: string;\n}): Promise<{\n em?: string;\n ph?: string;\n fn?: string;\n ln?: string;\n country?: string;\n st?: string;\n zp?: string;\n db?: string;\n}> {\n const [em, ph, fn, ln, country, st, zp, db] = await Promise.all([\n pii.email ? hashEmail(pii.email) : undefined,\n pii.phone ? hashPhone(pii.phone) : undefined,\n pii.firstName ? hashFirstName(pii.firstName) : undefined,\n pii.lastName ? hashLastName(pii.lastName) : undefined,\n pii.country ? hashCountry(pii.country) : undefined,\n pii.state ? hashState(pii.state) : undefined,\n pii.zip ? hashZip(pii.zip) : undefined,\n pii.dateOfBirth ? hashString(pii.dateOfBirth?.trim(), (v) => v) : undefined,\n ]);\n\n return {\n ...(em && { em }),\n ...(ph && { ph }),\n ...(fn && { fn }),\n ...(ln && { ln }),\n ...(country && { country }),\n ...(st && { st }),\n ...(zp && { zp }),\n ...(db && { db }),\n };\n}\n\n/**\n * Simple hash fallback for browsers without Web Crypto API\n * This uses a basic string hashing algorithm (not cryptographically secure)\n * @param value String to hash\n * @returns Hex-encoded hash string\n */\nfunction fallbackHash(value: string): string {\n let hash = 0;\n\n if (value.length === 0) {\n return \"0\";\n }\n\n for (let i = 0; i < value.length; i++) {\n const char = value.charCodeAt(i);\n hash = (hash << 5) - hash + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n\n return Math.abs(hash).toString(16).padStart(16, \"0\");\n}\n\n/**\n * Check if crypto API is available in the current environment\n * @returns true if Web Crypto API is available\n */\nexport function isCryptoAvailable(): boolean {\n return typeof crypto !== \"undefined\" && crypto.subtle !== undefined;\n}\n","/**\n * Facebook CAPI Service\n * Core business logic for Facebook Conversion API integration\n * Reusable across multiple projects\n *\n * Handles:\n * - Rate limiting\n * - Event ID generation\n * - fbc/fbp extraction\n * - Event formatting\n * - Retry logic with exponential backoff\n * - Request validation\n * - Timeout handling\n * - PII hashing (IP, email, phone, names)\n */\n\nimport { hashIP } from \"../utils/pii-hashing\";\n\n/**\n * Facebook CAPI Configuration\n */\nexport interface FacebookCAPIConfig {\n pixelId: string;\n accessToken: string;\n baseUrl: string;\n testEventCode?: string;\n}\n\n/**\n * CAPI Event Structure\n */\nexport interface CAPIEvent {\n event_name: string;\n event_time: number;\n event_id: string;\n action_source: \"website\" | \"app\";\n user_data: {\n client_ip_address?: string; // Now hashed in convertToCapiEvent\n client_user_agent?: string;\n fbc?: string;\n fbp?: string;\n // Hashed PII fields (optional)\n em?: string; // Hashed email\n ph?: string; // Hashed phone\n fn?: string; // Hashed first name\n ln?: string; // Hashed last name\n country?: string; // Hashed country code\n st?: string; // Hashed state\n zp?: string; // Hashed zip code\n db?: string; // Hashed date of birth\n external_id?: string; // Customer or order ID\n };\n custom_data: Record<string, any>;\n}\n\n/**\n * Rate Limiter - tracks requests per IP address\n * In-memory implementation (can be extended with Redis)\n */\nexport class RateLimiter {\n private store = new Map<string, { count: number; resetTime: number }>();\n private readonly limit: number;\n private readonly window: number;\n\n constructor(requestsPerMinute: number = 100) {\n this.limit = requestsPerMinute;\n this.window = 60 * 1000; // 1 minute\n }\n\n check(ip: string): { allowed: boolean; remaining: number } {\n const now = Date.now();\n let entry = this.store.get(ip);\n\n // Clean up expired entries\n if (entry && entry.resetTime < now) {\n this.store.delete(ip);\n entry = undefined;\n }\n\n if (!entry) {\n entry = { count: 0, resetTime: now + this.window };\n this.store.set(ip, entry);\n }\n\n const allowed = entry.count < this.limit;\n entry.count++;\n\n return {\n allowed,\n remaining: Math.max(0, this.limit - entry.count),\n };\n }\n}\n\n/**\n * Facebook CAPI Service\n * Core business logic for CAPI integration\n */\nexport class FacebookCAPIService {\n private rateLimiter = new RateLimiter(100); // 100 requests per minute per IP\n private readonly maxRetries = 3;\n private readonly baseDelay = 1000; // 1 second\n private readonly timeout = 30000; // 30 seconds\n private readonly debug = false; // Override in consuming projects if needed\n\n /**\n * Check if request is within rate limits\n */\n checkRateLimit(clientIp: string): { allowed: boolean; remaining: number } {\n return this.rateLimiter.check(clientIp);\n }\n\n /**\n * Generate a unique event ID for deduplication\n */\n generateEventId(eventName: string): string {\n const timestamp = Date.now();\n const randomString = Math.random().toString(36).substring(2, 8);\n return `${timestamp}_${randomString}_${eventName}`;\n }\n\n /**\n * Extract fbc cookie from request headers\n */\n extractFbcFromRequest(request: Request): string | undefined {\n const cookieHeader = request.headers.get(\"cookie\");\n if (!cookieHeader) return undefined;\n\n const cookies = cookieHeader.split(\";\");\n for (const cookie of cookies) {\n const [name, value] = cookie.trim().split(\"=\");\n if (name === \"_fbc\") {\n return value;\n }\n }\n\n return undefined;\n }\n\n /**\n * Get client IP address from request headers\n */\n getClientIpAddress(request: Request): string | undefined {\n const forwarded = request.headers.get(\"x-forwarded-for\");\n const realIp = request.headers.get(\"x-real-ip\");\n const cfConnectingIp = request.headers.get(\"cf-connecting-ip\");\n\n return forwarded?.split(\",\")[0] || realIp || cfConnectingIp || undefined;\n }\n\n /**\n * Convert event to Facebook CAPI format with optional PII hashing\n * Note: This method is now async to support SHA-256 IP hashing\n */\n async convertToCapiEvent(\n eventName: string,\n eventId: string,\n timestamp: number | undefined,\n enhancedParams: any,\n userInfo: any,\n ): Promise<CAPIEvent> {\n // Facebook CAPI expects Unix timestamp (seconds since epoch)\n const eventTime = timestamp\n ? Math.floor(timestamp / 1000)\n : Math.floor(Date.now() / 1000);\n\n // Hash IP address if provided (security best practice)\n let hashedIp: string | undefined;\n if (userInfo?.clientIpAddress) {\n try {\n hashedIp = await hashIP(userInfo.clientIpAddress);\n } catch (error) {\n console.error(\"[Facebook CAPI] Error hashing IP:\", error);\n // Fall back to unhashed if hashing fails\n hashedIp = userInfo.clientIpAddress;\n }\n }\n\n if (this.debug) {\n console.log(\"[Facebook CAPI] Event Conversion\", {\n eventName,\n eventId,\n eventTime,\n timestamp_input: timestamp,\n fbp: !!userInfo?.fbp,\n fbc: !!userInfo?.fbc,\n ip_hashed: !!hashedIp,\n pii_fields: {\n em: !!userInfo?.em,\n ph: !!userInfo?.ph,\n fn: !!userInfo?.fn,\n ln: !!userInfo?.ln,\n },\n });\n }\n\n const capiEvent: CAPIEvent = {\n event_name: eventName,\n event_time: eventTime,\n event_id: eventId,\n action_source: \"website\",\n user_data: {\n client_ip_address: hashedIp,\n client_user_agent: userInfo?.clientUserAgent,\n fbc: userInfo?.fbc,\n fbp: userInfo?.fbp,\n },\n custom_data: enhancedParams,\n };\n\n // Add optional hashed PII fields if provided\n if (userInfo?.em) {\n capiEvent.user_data.em = userInfo.em;\n }\n if (userInfo?.ph) {\n capiEvent.user_data.ph = userInfo.ph;\n }\n if (userInfo?.fn) {\n capiEvent.user_data.fn = userInfo.fn;\n }\n if (userInfo?.ln) {\n capiEvent.user_data.ln = userInfo.ln;\n }\n if (userInfo?.country) {\n capiEvent.user_data.country = userInfo.country;\n }\n if (userInfo?.st) {\n capiEvent.user_data.st = userInfo.st;\n }\n if (userInfo?.zp) {\n capiEvent.user_data.zp = userInfo.zp;\n }\n if (userInfo?.db) {\n capiEvent.user_data.db = userInfo.db;\n }\n if (userInfo?.external_id) {\n capiEvent.user_data.external_id = userInfo.external_id;\n }\n\n return capiEvent;\n }\n\n /**\n * Send event to Facebook CAPI with retry logic\n */\n async sendToFacebook(\n config: FacebookCAPIConfig,\n capiEvent: CAPIEvent,\n ): Promise<{\n success: boolean;\n data?: any;\n error?: string;\n pixelId: string;\n }> {\n for (let attempt = 0; attempt <= this.maxRetries; attempt++) {\n try {\n const payload = {\n data: [capiEvent],\n test_event_code: config.testEventCode,\n };\n\n if (this.debug) {\n console.log(\"[Facebook CAPI] Sending to Facebook\", {\n pixelId: config.pixelId,\n eventId: capiEvent.event_id,\n eventName: capiEvent.event_name,\n attempt: attempt + 1,\n });\n }\n\n const url = `${config.baseUrl}/${config.pixelId}/events?access_token=${config.accessToken}`;\n\n // Create timeout abort controller\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n try {\n const response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(payload),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n\n if (!response.ok) {\n // Retry on 5xx errors (server errors) and 429 (rate limit)\n if (\n (response.status >= 500 || response.status === 429) &&\n attempt < this.maxRetries\n ) {\n const delay = this.baseDelay * Math.pow(2, attempt);\n if (this.debug) {\n console.log(`[Facebook CAPI] Retrying in ${delay}ms`, {\n pixelId: config.pixelId,\n status: response.status,\n });\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue; // Retry\n }\n\n console.error(\n `Facebook CAPI Error for pixel ${config.pixelId} (attempt ${attempt + 1}/${this.maxRetries + 1}):`,\n { status: response.status },\n );\n return {\n success: false,\n error: `Facebook CAPI request failed: ${response.status}`,\n pixelId: config.pixelId,\n };\n }\n\n const result = await response.json();\n if (this.debug) {\n console.log(\n `Facebook CAPI: Events sent successfully to pixel ${config.pixelId}`,\n {\n events_received: result.events_received,\n },\n );\n }\n\n return {\n success: true,\n data: result,\n pixelId: config.pixelId,\n };\n } finally {\n clearTimeout(timeoutId);\n }\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : String(error);\n\n // Retry on network errors and timeouts\n if (attempt < this.maxRetries) {\n const delay = this.baseDelay * Math.pow(2, attempt);\n if (this.debug) {\n console.log(`[Facebook CAPI] Retrying in ${delay}ms after error`, {\n pixelId: config.pixelId,\n error: errorMessage,\n });\n }\n await new Promise((resolve) => setTimeout(resolve, delay));\n continue; // Retry\n }\n\n console.error(\n `Facebook CAPI Network Error for pixel ${config.pixelId} (attempt ${attempt + 1}/${this.maxRetries + 1}):`,\n { error: errorMessage },\n );\n return {\n success: false,\n error: `Network error: ${errorMessage}`,\n pixelId: config.pixelId,\n };\n }\n }\n\n return {\n success: false,\n error: \"Failed after all retry attempts\",\n pixelId: config.pixelId,\n };\n }\n\n /**\n * Validate request body\n */\n validateRequestBody(\n eventName: string,\n enhancedParams: any,\n bodySize: number,\n ): { valid: boolean; error?: string } {\n // Validate required fields\n if (!eventName || !enhancedParams) {\n return {\n valid: false,\n error: \"eventName and enhancedParams are required\",\n };\n }\n\n // Validate body size (max 1MB)\n if (bodySize > 1024 * 1024) {\n return {\n valid: false,\n error: \"Request body too large (max 1MB)\",\n };\n }\n\n // Validate eventName format (alphanumeric and underscore only)\n if (!/^[a-zA-Z0-9_]+$/.test(eventName)) {\n return {\n valid: false,\n error:\n \"eventName must contain only alphanumeric characters and underscores\",\n };\n }\n\n return { valid: true };\n }\n}\n\n/**\n * Singleton instance of the service\n */\nexport const facebookCAPIService = new FacebookCAPIService();\n"],"mappings":"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","names":["useEffect","useEffect","logger"]}
|
|
1
|
+
{"version":3,"sources":["../src/ShopkitAnalytics.tsx","../src/shopify/ShopifyAnalyticsScript.tsx"],"sourcesContent":["\"use client\";\n\n/**\n * ShopkitAnalytics Component\n *\n * Main analytics provider component that initializes and manages\n * all configured analytics adapters.\n *\n * @module @shopkit/analytics\n */\n\nimport React, { useEffect } from \"react\";\nimport { initializeEventTracking } from \"./events/init\";\nimport { AffiliateTracker } from \"./affiliate\";\nimport { createLogger, type LoggerConfig } from \"./logger\";\nimport {\n GoogleAdapter,\n MultiPixelAdapter,\n MoengageAdapter,\n PostHogAdapter,\n ShopifyAdapter,\n} from \"./adapters\";\nimport type {\n GoogleAdapterConfig,\n MultiPixelAdapterConfig,\n MoengageAdapterConfig,\n PostHogAdapterConfig,\n ShopifyAdapterConfig,\n} from \"./adapters\";\nimport type { TrackingAdapter } from \"./events/subscriber\";\nimport type { AffiliateConfig } from \"./affiliate/types\";\nimport { ShopifyAnalyticsScript } from \"./shopify/ShopifyAnalyticsScript\";\n\n/**\n * Configuration for ShopkitAnalytics component\n *\n * @example\n * ```typescript\n * const config: ShopkitAnalyticsConfig = {\n * googleAnalytics: { measurementId: 'G-XXXXXX' },\n * facebookPixel: { pixelId: '123456789' },\n * posthog: { apiKey: 'phc_xxx', apiHost: 'https://app.posthog.com' },\n * logger: { enabled: true, level: 'info' },\n * };\n * ```\n */\nexport interface ShopkitAnalyticsConfig {\n // Analytics Adapters\n googleAnalytics?: GoogleAdapterConfig;\n facebookMultiPixel?: MultiPixelAdapterConfig;\n moengage?: MoengageAdapterConfig;\n posthog?: PostHogAdapterConfig;\n shopify?: ShopifyAdapterConfig;\n customAdapters?: TrackingAdapter[];\n\n // Affiliate Tracking\n affiliate?: {\n enabled?: boolean;\n config?: Partial<AffiliateConfig>;\n autoCapture?: boolean;\n };\n\n // Logger Settings\n logger?: LoggerConfig;\n\n // General Settings (deprecated - use logger.enabled instead)\n enableDebugLogs?: boolean;\n autoInitialize?: boolean;\n}\n\n/**\n * Props for ShopkitAnalytics component\n */\nexport interface ShopkitAnalyticsProps {\n /** Analytics configuration object */\n config: ShopkitAnalyticsConfig;\n /** Optional children to render */\n children?: React.ReactNode;\n /** Callback when analytics initialization completes */\n onInitialized?: () => void;\n /** Callback when an error occurs during initialization */\n onError?: (error: Error) => void;\n}\n\n/**\n * ShopkitAnalytics Component\n *\n * The main analytics provider that initializes all configured tracking adapters\n * and sets up event publishing infrastructure.\n *\n * Place this component in your app's root layout to enable analytics tracking\n * throughout your application.\n *\n * @param props - Component props\n * @returns React component with analytics initialization and affiliate tracking\n *\n * @example\n * ```typescript\n * // In your root layout\n * import { ShopkitAnalytics } from '@shopkit/analytics';\n *\n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <body>\n * <ShopkitAnalytics\n * config={{\n * googleAnalytics: { measurementId: 'G-XXXXXX' },\n * facebookPixel: { pixelId: '123456789' },\n * }}\n * onInitialized={() => console.log('Analytics ready')}\n * onError={(err) => console.error('Analytics error:', err)}\n * >\n * {children}\n * </ShopkitAnalytics>\n * </body>\n * </html>\n * );\n * }\n * ```\n */\nconst ShopkitAnalytics: React.FC<ShopkitAnalyticsProps> = ({\n config,\n children,\n onInitialized,\n onError,\n}) => {\n useEffect(() => {\n const initializeAnalytics = async () => {\n if (config.autoInitialize === false) return;\n\n // Create logger instance\n const loggerConfig: LoggerConfig = {\n enabled: config.logger?.enabled ?? config.enableDebugLogs ?? true,\n level: config.logger?.level ?? \"info\",\n name: \"@shopkit/analytics\",\n prettyPrint: config.logger?.prettyPrint ?? false,\n };\n\n const logger = createLogger(loggerConfig);\n\n try {\n logger.info(\"Starting initialization...\");\n\n const adapters: TrackingAdapter[] = [];\n\n // Helper function to add logger config to adapter config\n const addLoggerConfig = (adapterConfig: any) => ({\n ...adapterConfig,\n enableDebugLogs: loggerConfig.enabled,\n logLevel: loggerConfig.level,\n });\n\n // Google Analytics 4\n if (config.googleAnalytics) {\n const gaAdapter = new GoogleAdapter(\n addLoggerConfig(config.googleAnalytics),\n );\n adapters.push(gaAdapter);\n logger.info(\"Added Google Analytics adapter\");\n }\n\n // Facebook Multi-Pixel\n if (config.facebookMultiPixel) {\n const multiPixelAdapter = new MultiPixelAdapter(\n addLoggerConfig(config.facebookMultiPixel),\n );\n adapters.push(multiPixelAdapter);\n logger.info(\n `Added Facebook Multi-Pixel adapter with ${config.facebookMultiPixel.pixels.length} pixels`,\n );\n }\n\n // MoEngage\n if (config.moengage) {\n const moengageAdapter = new MoengageAdapter(\n addLoggerConfig(config.moengage),\n );\n adapters.push(moengageAdapter);\n logger.info(\"Added MoEngage adapter\");\n }\n\n // PostHog\n if (config.posthog) {\n const posthogAdapter = new PostHogAdapter(\n addLoggerConfig(config.posthog),\n );\n adapters.push(posthogAdapter);\n logger.info(\"Added PostHog adapter\");\n }\n\n // Shopify Analytics\n if (config.shopify) {\n const shopifyAdapter = new ShopifyAdapter(\n addLoggerConfig(config.shopify),\n );\n adapters.push(shopifyAdapter);\n logger.info(\"Added Shopify Analytics adapter\");\n }\n\n // Custom adapters\n if (config.customAdapters) {\n adapters.push(...config.customAdapters);\n logger.info(`Added ${config.customAdapters.length} custom adapters`);\n }\n\n if (adapters.length === 0) {\n logger.warn(\"No analytics adapters configured\");\n } else {\n // Initialize the event tracking system\n await initializeEventTracking(adapters);\n logger.info(\"Event tracking initialized with adapters\", {\n adapters: adapters.map((a) => a.name),\n });\n }\n\n logger.info(\"Initialization completed successfully\");\n onInitialized?.();\n } catch (err) {\n const error =\n err instanceof Error\n ? err\n : new Error(\"Unknown initialization error\");\n logger.error(\"Initialization failed\", error);\n onError?.(error);\n }\n };\n\n initializeAnalytics();\n }, [config, onInitialized, onError]);\n\n return (\n <>\n {/* Affiliate Tracker */}\n {config.affiliate?.enabled !== false && (\n <AffiliateTracker\n config={config.affiliate?.config}\n autoCapture={config.affiliate?.autoCapture}\n />\n )}\n\n {/* Shopify Analytics Script - Only render if Shopify config is provided */}\n {config.shopify?.domain && (\n <ShopifyAnalyticsScript domain={config.shopify.domain} />\n )}\n\n {children ? <>{children}</> : null}\n </>\n );\n};\n\nexport default ShopkitAnalytics;\n","\"use client\";\n\nimport { useShopifyCookies } from \"@shopify/hydrogen-react\";\nimport { useEffect, useState } from \"react\";\nimport { EventType, publishEvent } from \"../events\";\n\n// Safe pathname hook that works in different environments\nfunction usePathnameCompat() {\n const [pathname, setPathname] = useState<string>(\"\");\n\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n setPathname(window.location.pathname);\n \n // Listen for navigation changes\n const handleLocationChange = () => {\n setPathname(window.location.pathname);\n };\n\n // Listen for popstate (back/forward navigation)\n window.addEventListener(\"popstate\", handleLocationChange);\n \n // For SPA navigation, we might need to listen to custom events\n // This is a fallback for when Next.js router is not available\n \n return () => {\n window.removeEventListener(\"popstate\", handleLocationChange);\n };\n }\n }, []);\n\n return pathname;\n}\n\n/**\n * Component to initialize Shopify Analytics cookies\n * This component handles the useShopifyCookies hook which must be used in a React component\n */\n\ninterface ShopifyAnalyticsScriptProps {\n domain: string;\n}\n\nexport function ShopifyAnalyticsScript({\n domain,\n}: ShopifyAnalyticsScriptProps) {\n const pathname = usePathnameCompat();\n\n const [sessionValid, setSessionValid] = useState(false);\n\n useShopifyCookies({\n hasUserConsent: true,\n domain: domain,\n });\n\n useEffect(() => {\n // Validate session after cookie initialization\n const validateSession = () => {\n const shopifyY = document.cookie.includes(\"_shopify_y=\");\n const shopifyS = document.cookie.includes(\"_shopify_s=\");\n\n if (shopifyY && shopifyS) {\n setSessionValid(true);\n console.log(\"✅ Shopify Analytics session validated\");\n } else {\n console.warn(\"⚠️ Shopify session cookies missing\");\n }\n };\n\n // Allow time for cookies to be set\n const timer = setTimeout(validateSession, 1000);\n return () => clearTimeout(timer);\n }, []);\n\n // Expose session status for debugging\n useEffect(() => {\n if (typeof window !== \"undefined\") {\n (window as any).__shopifySessionValid = sessionValid;\n }\n }, [sessionValid]);\n\n useEffect(() => {\n const trackPageView = () => {\n publishEvent({\n type: EventType.SHOPIFY_PAGE_VIEW,\n });\n };\n\n const timer = setTimeout(trackPageView, 1500);\n return () => clearTimeout(timer);\n }, [pathname]);\n\n return null; // This component doesn't render anything\n}\n"],"mappings":"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","names":["useEffect","useEffect","logger"]}
|
package/dist/types.d.mts
CHANGED
|
@@ -23,26 +23,6 @@ declare enum EventType {
|
|
|
23
23
|
USER_SIGNUP = "user_signup",
|
|
24
24
|
/** User login tracking */
|
|
25
25
|
USER_LOGIN = "user_login",
|
|
26
|
-
/** Checkout initiation */
|
|
27
|
-
STARTED_CHECKOUT_GK = "started_checkout_gk",
|
|
28
|
-
/** Contact information collection */
|
|
29
|
-
MOBILE_ADDED_GK = "mobile_added_gk",
|
|
30
|
-
/** Address management events */
|
|
31
|
-
PIN_CODE_ADDED_GK = "pin_code_added_gk",
|
|
32
|
-
ADDRESS_ADDED_GK = "address_added_gk",
|
|
33
|
-
ADDRESS_SELECTED_GK = "address_selected_gk",
|
|
34
|
-
ADDRESS_COMPLETED_GK = "address_completed_gk",
|
|
35
|
-
/** Payment flow events */
|
|
36
|
-
PAYMENT_METHOD_SELECTED_GK = "payment_method_selected_gk",
|
|
37
|
-
PAYMENT_COMPLETED_GK = "payment_completed_gk",
|
|
38
|
-
/** Order completion events */
|
|
39
|
-
ORDER_SUCCESS = "order_success",
|
|
40
|
-
ORDER_COMPLETED = "order_completed",
|
|
41
|
-
/** Product interaction tracking through KwikPass */
|
|
42
|
-
PRODUCT_VIEW_KP = "product_view_kp",
|
|
43
|
-
COLLECTION_VIEW_KP = "collection_view_kp",
|
|
44
|
-
/** Page navigation tracking through KwikPass */
|
|
45
|
-
PAGE_VIEW_KP = "page_view_kp",
|
|
46
26
|
/** Shopify analytics integration - Do Not Remove */
|
|
47
27
|
SHOPIFY_PAGE_VIEW = "shopify_page_view",
|
|
48
28
|
/** Generic custom event for any tracking needs */
|
|
@@ -242,113 +222,6 @@ interface ICustomEvent extends BaseEvent {
|
|
|
242
222
|
interface ISpecificEvent extends BaseEvent {
|
|
243
223
|
type: EventType.SPECIFIC;
|
|
244
224
|
}
|
|
245
|
-
/**
|
|
246
|
-
* Started checkout GK event - Tracks when user initiates GoKwik checkout
|
|
247
|
-
*/
|
|
248
|
-
interface IStartedCheckoutGKEvent extends BaseEvent {
|
|
249
|
-
type: EventType.STARTED_CHECKOUT_GK;
|
|
250
|
-
cartValue?: number;
|
|
251
|
-
currency?: string;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Mobile added GK event - Tracks when user adds mobile number in GoKwik checkout
|
|
255
|
-
*/
|
|
256
|
-
interface IMobileAddedGKEvent extends BaseEvent {
|
|
257
|
-
type: EventType.MOBILE_ADDED_GK;
|
|
258
|
-
mobile?: string;
|
|
259
|
-
}
|
|
260
|
-
/**
|
|
261
|
-
* PIN code added GK event - Tracks when user adds PIN code for delivery
|
|
262
|
-
*/
|
|
263
|
-
interface IPinCodeAddedGKEvent extends BaseEvent {
|
|
264
|
-
type: EventType.PIN_CODE_ADDED_GK;
|
|
265
|
-
pinCode?: string;
|
|
266
|
-
}
|
|
267
|
-
/**
|
|
268
|
-
* Address added GK event - Tracks when user adds a new address
|
|
269
|
-
*/
|
|
270
|
-
interface IAddressAddedGKEvent extends BaseEvent {
|
|
271
|
-
type: EventType.ADDRESS_ADDED_GK;
|
|
272
|
-
addressId?: string;
|
|
273
|
-
}
|
|
274
|
-
/**
|
|
275
|
-
* Address selected GK event - Tracks when user selects an existing address
|
|
276
|
-
*/
|
|
277
|
-
interface IAddressSelectedGKEvent extends BaseEvent {
|
|
278
|
-
type: EventType.ADDRESS_SELECTED_GK;
|
|
279
|
-
addressId?: string;
|
|
280
|
-
}
|
|
281
|
-
/**
|
|
282
|
-
* Address completed GK event - Tracks when user completes address information
|
|
283
|
-
*/
|
|
284
|
-
interface IAddressCompletedGKEvent extends BaseEvent {
|
|
285
|
-
type: EventType.ADDRESS_COMPLETED_GK;
|
|
286
|
-
addressId?: string;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* Payment method selected GK event - Tracks when user selects payment method
|
|
290
|
-
*/
|
|
291
|
-
interface IPaymentMethodSelectedGKEvent extends BaseEvent {
|
|
292
|
-
type: EventType.PAYMENT_METHOD_SELECTED_GK;
|
|
293
|
-
paymentMethod?: string;
|
|
294
|
-
}
|
|
295
|
-
/**
|
|
296
|
-
* Payment completed GK event - Tracks when user completes payment
|
|
297
|
-
*/
|
|
298
|
-
interface IPaymentCompletedGKEvent extends BaseEvent {
|
|
299
|
-
type: EventType.PAYMENT_COMPLETED_GK;
|
|
300
|
-
amount?: number;
|
|
301
|
-
currency?: string;
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Order success event - Tracks successful order completion
|
|
305
|
-
*/
|
|
306
|
-
interface IOrderSuccessEvent extends BaseEvent {
|
|
307
|
-
type: EventType.ORDER_SUCCESS;
|
|
308
|
-
orderId?: string;
|
|
309
|
-
amount?: number;
|
|
310
|
-
currency?: string;
|
|
311
|
-
}
|
|
312
|
-
/**
|
|
313
|
-
* Order completed event - Tracks final order completion confirmation
|
|
314
|
-
*/
|
|
315
|
-
interface IOrderCompletedEvent extends BaseEvent {
|
|
316
|
-
type: EventType.ORDER_COMPLETED;
|
|
317
|
-
orderId?: string;
|
|
318
|
-
amount?: number;
|
|
319
|
-
currency?: string;
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Product view KP event - Tracks when user views a product through KwikPass
|
|
323
|
-
*/
|
|
324
|
-
interface IProductViewKPEvent extends BaseEvent {
|
|
325
|
-
type: EventType.PRODUCT_VIEW_KP;
|
|
326
|
-
productId?: string;
|
|
327
|
-
productName?: string;
|
|
328
|
-
price?: number;
|
|
329
|
-
currency?: string;
|
|
330
|
-
category?: string;
|
|
331
|
-
}
|
|
332
|
-
/**
|
|
333
|
-
* Collection view KP event - Tracks when user views a product collection through KwikPass
|
|
334
|
-
*/
|
|
335
|
-
interface ICollectionViewKPEvent extends BaseEvent {
|
|
336
|
-
type: EventType.COLLECTION_VIEW_KP;
|
|
337
|
-
productId?: string;
|
|
338
|
-
productName?: string;
|
|
339
|
-
price?: number;
|
|
340
|
-
currency?: string;
|
|
341
|
-
category?: string;
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* Page view KP event - Tracks general page views through KwikPass
|
|
345
|
-
*/
|
|
346
|
-
interface IPageViewKPEvent extends BaseEvent {
|
|
347
|
-
type: EventType.PAGE_VIEW_KP;
|
|
348
|
-
path?: string;
|
|
349
|
-
title?: string;
|
|
350
|
-
referrer?: string;
|
|
351
|
-
}
|
|
352
225
|
/**
|
|
353
226
|
* Shopify page view event - Tracks page views specifically for Shopify analytics
|
|
354
227
|
*/
|
|
@@ -359,6 +232,6 @@ interface IShopifyPageViewEvent extends BaseEvent {
|
|
|
359
232
|
/**
|
|
360
233
|
* Union type of all possible events
|
|
361
234
|
*/
|
|
362
|
-
type TEvent = IPageViewEvent | IAddToCartEvent | IRemoveFromCartEvent | ICartViewedEvent | ISearchEvent | IUserSignupEvent | IUserLoginEvent | IViewContentEvent | IPurchaseEvent | IViewSearchResultsEvent | IBeginCheckoutEvent | ICustomEvent | ISpecificEvent | IAddPaymentInfoEvent |
|
|
235
|
+
type TEvent = IPageViewEvent | IAddToCartEvent | IRemoveFromCartEvent | ICartViewedEvent | ISearchEvent | IUserSignupEvent | IUserLoginEvent | IViewContentEvent | IPurchaseEvent | IViewSearchResultsEvent | IBeginCheckoutEvent | ICustomEvent | ISpecificEvent | IAddPaymentInfoEvent | IViewedProductEvent | IShopifyPageViewEvent;
|
|
363
236
|
|
|
364
|
-
export { type BaseEvent, EventType, type IAddPaymentInfoEvent, type IAddToCartEvent, type
|
|
237
|
+
export { type BaseEvent, EventType, type IAddPaymentInfoEvent, type IAddToCartEvent, type IBeginCheckoutEvent, type ICartViewedEvent, type ICustomEvent, type IPageViewEvent, type IPurchaseEvent, type IRemoveFromCartEvent, type ISearchEvent, type IShopifyPageViewEvent, type ISpecificEvent, type IUserLoginEvent, type IUserSignupEvent, type IViewContentEvent, type IViewSearchResultsEvent, type IViewedProductEvent, type TEvent };
|