shopkit-analytics 1.1.3 → 1.2.1
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.js +9 -14
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/index.mjs +1 -1
- 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-T4CBWUTQ.mjs → chunk-YJE5NOFF.mjs} +10 -15
- package/dist/chunk-YJE5NOFF.mjs.map +1 -0
- package/dist/{chunk-ASHIIHZ4.mjs → chunk-ZTIVTB5J.mjs} +2 -2
- package/dist/events/index.js +9 -14
- package/dist/events/index.js.map +1 -1
- package/dist/events/index.mjs +2 -2
- package/dist/index.d.mts +3 -122
- package/dist/index.d.ts +3 -122
- package/dist/index.js +9 -333
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -318
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-T4CBWUTQ.mjs.map +0 -1
- /package/dist/{chunk-ASHIIHZ4.mjs.map → chunk-ZTIVTB5J.mjs.map} +0 -0
package/dist/events/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
initTracking,
|
|
3
3
|
initializeEventTracking
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-ZTIVTB5J.mjs";
|
|
5
|
+
import "../chunk-YJE5NOFF.mjs";
|
|
6
6
|
import "../chunk-2DA66EMD.mjs";
|
|
7
7
|
import "../chunk-UFDN3A6M.mjs";
|
|
8
8
|
import {
|
package/dist/index.d.mts
CHANGED
|
@@ -3,8 +3,8 @@ import { G as GoogleAdapterConfig, M as MultiPixelAdapterConfig, a as MoengageAd
|
|
|
3
3
|
export { B as BaseAdapter, c as GoogleAdapter, g as Logger, d as MoengageAdapter, b as MultiPixelAdapter, e as PostHogAdapter, f as ShopifyAdapter, i as createAdapterLogger, h as createLogger, l as logger } from './index-fYvOG_to.mjs';
|
|
4
4
|
import { T as TrackingAdapter } from './subscriber-90r_t90W.mjs';
|
|
5
5
|
export { I as IAdapterParams, b as IBaseAdapterParams, a as TAdapterParams, e as eventSubscriber } from './subscriber-90r_t90W.mjs';
|
|
6
|
-
import {
|
|
7
|
-
export {
|
|
6
|
+
import { A as AffiliateConfig } from './affiliate-tracker-BgHwibPv.mjs';
|
|
7
|
+
export { k as AffiliateData, j as AffiliateParams, a as AffiliateTracker, i as AffiliateTrackerProps, d as captureAffiliateParams, e as clearAffiliateParams, g as getAffiliateParams, f as getAffiliateSource, h as hasAffiliateData, c as useAffiliateSource, u as useAffiliateTracker, b as useHasAffiliateData } from './affiliate-tracker-BgHwibPv.mjs';
|
|
8
8
|
export { BaseEvent, EventType, IAddPaymentInfoEvent, IAddToCartEvent, IBeginCheckoutEvent, ICartViewedEvent, ICustomEvent, IPageViewEvent, IPurchaseEvent, IRemoveFromCartEvent, ISearchEvent, IShopifyPageViewEvent, ISpecificEvent, IUserLoginEvent, IUserSignupEvent, IViewContentEvent, IViewSearchResultsEvent, IViewedProductEvent, TEvent } from './types.mjs';
|
|
9
9
|
export { eventPublisher, initTracking, initializeEventTracking, publishEvent } from './events/index.mjs';
|
|
10
10
|
export { E as ExperimentData, P as PRIMA_EXPERIMENT_COOKIES, a as PRIMA_EXPERIMENT_COOKIE_NAMES } from './types-BBZbvq9-.mjs';
|
|
@@ -102,125 +102,6 @@ interface ShopkitAnalyticsProps {
|
|
|
102
102
|
*/
|
|
103
103
|
declare const ShopkitAnalytics: React.FC<ShopkitAnalyticsProps>;
|
|
104
104
|
|
|
105
|
-
/**
|
|
106
|
-
* Facebook CAPI Service
|
|
107
|
-
* Core business logic for Facebook Conversion API integration
|
|
108
|
-
* Reusable across multiple projects
|
|
109
|
-
*
|
|
110
|
-
* Handles:
|
|
111
|
-
* - Rate limiting
|
|
112
|
-
* - Event ID generation
|
|
113
|
-
* - fbc/fbp extraction
|
|
114
|
-
* - Event formatting
|
|
115
|
-
* - Retry logic with exponential backoff
|
|
116
|
-
* - Request validation
|
|
117
|
-
* - Timeout handling
|
|
118
|
-
* - PII hashing (IP, email, phone, names)
|
|
119
|
-
*/
|
|
120
|
-
/**
|
|
121
|
-
* Facebook CAPI Configuration
|
|
122
|
-
*/
|
|
123
|
-
interface FacebookCAPIConfig {
|
|
124
|
-
pixelId: string;
|
|
125
|
-
accessToken: string;
|
|
126
|
-
baseUrl: string;
|
|
127
|
-
testEventCode?: string;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* CAPI Event Structure
|
|
131
|
-
*/
|
|
132
|
-
interface CAPIEvent {
|
|
133
|
-
event_name: string;
|
|
134
|
-
event_time: number;
|
|
135
|
-
event_id: string;
|
|
136
|
-
action_source: "website" | "app";
|
|
137
|
-
user_data: {
|
|
138
|
-
client_ip_address?: string;
|
|
139
|
-
client_user_agent?: string;
|
|
140
|
-
fbc?: string;
|
|
141
|
-
fbp?: string;
|
|
142
|
-
em?: string;
|
|
143
|
-
ph?: string;
|
|
144
|
-
fn?: string;
|
|
145
|
-
ln?: string;
|
|
146
|
-
country?: string;
|
|
147
|
-
st?: string;
|
|
148
|
-
zp?: string;
|
|
149
|
-
db?: string;
|
|
150
|
-
external_id?: string;
|
|
151
|
-
};
|
|
152
|
-
custom_data: Record<string, any>;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Rate Limiter - tracks requests per IP address
|
|
156
|
-
* In-memory implementation (can be extended with Redis)
|
|
157
|
-
*/
|
|
158
|
-
declare class RateLimiter {
|
|
159
|
-
private store;
|
|
160
|
-
private readonly limit;
|
|
161
|
-
private readonly window;
|
|
162
|
-
constructor(requestsPerMinute?: number);
|
|
163
|
-
check(ip: string): {
|
|
164
|
-
allowed: boolean;
|
|
165
|
-
remaining: number;
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Facebook CAPI Service
|
|
170
|
-
* Core business logic for CAPI integration
|
|
171
|
-
*/
|
|
172
|
-
declare class FacebookCAPIService {
|
|
173
|
-
private rateLimiter;
|
|
174
|
-
private readonly maxRetries;
|
|
175
|
-
private readonly baseDelay;
|
|
176
|
-
private readonly timeout;
|
|
177
|
-
private readonly debug;
|
|
178
|
-
/**
|
|
179
|
-
* Check if request is within rate limits
|
|
180
|
-
*/
|
|
181
|
-
checkRateLimit(clientIp: string): {
|
|
182
|
-
allowed: boolean;
|
|
183
|
-
remaining: number;
|
|
184
|
-
};
|
|
185
|
-
/**
|
|
186
|
-
* Generate a unique event ID for deduplication
|
|
187
|
-
*/
|
|
188
|
-
generateEventId(eventName: string): string;
|
|
189
|
-
/**
|
|
190
|
-
* Extract fbc cookie from request headers
|
|
191
|
-
*/
|
|
192
|
-
extractFbcFromRequest(request: Request): string | undefined;
|
|
193
|
-
/**
|
|
194
|
-
* Get client IP address from request headers
|
|
195
|
-
*/
|
|
196
|
-
getClientIpAddress(request: Request): string | undefined;
|
|
197
|
-
/**
|
|
198
|
-
* Convert event to Facebook CAPI format with optional PII hashing
|
|
199
|
-
* Note: This method is now async to support SHA-256 IP hashing
|
|
200
|
-
*/
|
|
201
|
-
convertToCapiEvent(eventName: string, eventId: string, timestamp: number | undefined, enhancedParams: any, userInfo: any): Promise<CAPIEvent>;
|
|
202
|
-
/**
|
|
203
|
-
* Send event to Facebook CAPI with retry logic
|
|
204
|
-
*/
|
|
205
|
-
sendToFacebook(config: FacebookCAPIConfig, capiEvent: CAPIEvent): Promise<{
|
|
206
|
-
success: boolean;
|
|
207
|
-
data?: any;
|
|
208
|
-
error?: string;
|
|
209
|
-
pixelId: string;
|
|
210
|
-
}>;
|
|
211
|
-
/**
|
|
212
|
-
* Validate request body
|
|
213
|
-
*/
|
|
214
|
-
validateRequestBody(eventName: string, enhancedParams: any, bodySize: number): {
|
|
215
|
-
valid: boolean;
|
|
216
|
-
error?: string;
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Singleton instance of the service
|
|
221
|
-
*/
|
|
222
|
-
declare const facebookCAPIService: FacebookCAPIService;
|
|
223
|
-
|
|
224
105
|
/**
|
|
225
106
|
* Component to initialize Shopify Analytics cookies
|
|
226
107
|
* This component handles the useShopifyCookies hook which must be used in a React component
|
|
@@ -230,4 +111,4 @@ interface ShopifyAnalyticsScriptProps {
|
|
|
230
111
|
}
|
|
231
112
|
declare function ShopifyAnalyticsScript({ domain, }: ShopifyAnalyticsScriptProps): null;
|
|
232
113
|
|
|
233
|
-
export { AffiliateConfig,
|
|
114
|
+
export { AffiliateConfig, GoogleAdapterConfig, LoggerConfig, MoengageAdapterConfig, MultiPixelAdapterConfig, PostHogAdapterConfig, ShopifyAdapterConfig, ShopifyAnalyticsScript, ShopkitAnalytics, type ShopkitAnalyticsConfig, type ShopkitAnalyticsProps, TrackingAdapter, ShopkitAnalytics as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ import { G as GoogleAdapterConfig, M as MultiPixelAdapterConfig, a as MoengageAd
|
|
|
3
3
|
export { B as BaseAdapter, c as GoogleAdapter, g as Logger, d as MoengageAdapter, b as MultiPixelAdapter, e as PostHogAdapter, f as ShopifyAdapter, i as createAdapterLogger, h as createLogger, l as logger } from './index-B-TnPt4F.js';
|
|
4
4
|
import { T as TrackingAdapter } from './subscriber-AtiHiP3i.js';
|
|
5
5
|
export { I as IAdapterParams, b as IBaseAdapterParams, a as TAdapterParams, e as eventSubscriber } from './subscriber-AtiHiP3i.js';
|
|
6
|
-
import {
|
|
7
|
-
export {
|
|
6
|
+
import { A as AffiliateConfig } from './affiliate-tracker-BgHwibPv.js';
|
|
7
|
+
export { k as AffiliateData, j as AffiliateParams, a as AffiliateTracker, i as AffiliateTrackerProps, d as captureAffiliateParams, e as clearAffiliateParams, g as getAffiliateParams, f as getAffiliateSource, h as hasAffiliateData, c as useAffiliateSource, u as useAffiliateTracker, b as useHasAffiliateData } from './affiliate-tracker-BgHwibPv.js';
|
|
8
8
|
export { BaseEvent, EventType, IAddPaymentInfoEvent, IAddToCartEvent, IBeginCheckoutEvent, ICartViewedEvent, ICustomEvent, IPageViewEvent, IPurchaseEvent, IRemoveFromCartEvent, ISearchEvent, IShopifyPageViewEvent, ISpecificEvent, IUserLoginEvent, IUserSignupEvent, IViewContentEvent, IViewSearchResultsEvent, IViewedProductEvent, TEvent } from './types.js';
|
|
9
9
|
export { eventPublisher, initTracking, initializeEventTracking, publishEvent } from './events/index.js';
|
|
10
10
|
export { E as ExperimentData, P as PRIMA_EXPERIMENT_COOKIES, a as PRIMA_EXPERIMENT_COOKIE_NAMES } from './types-BBZbvq9-.js';
|
|
@@ -102,125 +102,6 @@ interface ShopkitAnalyticsProps {
|
|
|
102
102
|
*/
|
|
103
103
|
declare const ShopkitAnalytics: React.FC<ShopkitAnalyticsProps>;
|
|
104
104
|
|
|
105
|
-
/**
|
|
106
|
-
* Facebook CAPI Service
|
|
107
|
-
* Core business logic for Facebook Conversion API integration
|
|
108
|
-
* Reusable across multiple projects
|
|
109
|
-
*
|
|
110
|
-
* Handles:
|
|
111
|
-
* - Rate limiting
|
|
112
|
-
* - Event ID generation
|
|
113
|
-
* - fbc/fbp extraction
|
|
114
|
-
* - Event formatting
|
|
115
|
-
* - Retry logic with exponential backoff
|
|
116
|
-
* - Request validation
|
|
117
|
-
* - Timeout handling
|
|
118
|
-
* - PII hashing (IP, email, phone, names)
|
|
119
|
-
*/
|
|
120
|
-
/**
|
|
121
|
-
* Facebook CAPI Configuration
|
|
122
|
-
*/
|
|
123
|
-
interface FacebookCAPIConfig {
|
|
124
|
-
pixelId: string;
|
|
125
|
-
accessToken: string;
|
|
126
|
-
baseUrl: string;
|
|
127
|
-
testEventCode?: string;
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* CAPI Event Structure
|
|
131
|
-
*/
|
|
132
|
-
interface CAPIEvent {
|
|
133
|
-
event_name: string;
|
|
134
|
-
event_time: number;
|
|
135
|
-
event_id: string;
|
|
136
|
-
action_source: "website" | "app";
|
|
137
|
-
user_data: {
|
|
138
|
-
client_ip_address?: string;
|
|
139
|
-
client_user_agent?: string;
|
|
140
|
-
fbc?: string;
|
|
141
|
-
fbp?: string;
|
|
142
|
-
em?: string;
|
|
143
|
-
ph?: string;
|
|
144
|
-
fn?: string;
|
|
145
|
-
ln?: string;
|
|
146
|
-
country?: string;
|
|
147
|
-
st?: string;
|
|
148
|
-
zp?: string;
|
|
149
|
-
db?: string;
|
|
150
|
-
external_id?: string;
|
|
151
|
-
};
|
|
152
|
-
custom_data: Record<string, any>;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Rate Limiter - tracks requests per IP address
|
|
156
|
-
* In-memory implementation (can be extended with Redis)
|
|
157
|
-
*/
|
|
158
|
-
declare class RateLimiter {
|
|
159
|
-
private store;
|
|
160
|
-
private readonly limit;
|
|
161
|
-
private readonly window;
|
|
162
|
-
constructor(requestsPerMinute?: number);
|
|
163
|
-
check(ip: string): {
|
|
164
|
-
allowed: boolean;
|
|
165
|
-
remaining: number;
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Facebook CAPI Service
|
|
170
|
-
* Core business logic for CAPI integration
|
|
171
|
-
*/
|
|
172
|
-
declare class FacebookCAPIService {
|
|
173
|
-
private rateLimiter;
|
|
174
|
-
private readonly maxRetries;
|
|
175
|
-
private readonly baseDelay;
|
|
176
|
-
private readonly timeout;
|
|
177
|
-
private readonly debug;
|
|
178
|
-
/**
|
|
179
|
-
* Check if request is within rate limits
|
|
180
|
-
*/
|
|
181
|
-
checkRateLimit(clientIp: string): {
|
|
182
|
-
allowed: boolean;
|
|
183
|
-
remaining: number;
|
|
184
|
-
};
|
|
185
|
-
/**
|
|
186
|
-
* Generate a unique event ID for deduplication
|
|
187
|
-
*/
|
|
188
|
-
generateEventId(eventName: string): string;
|
|
189
|
-
/**
|
|
190
|
-
* Extract fbc cookie from request headers
|
|
191
|
-
*/
|
|
192
|
-
extractFbcFromRequest(request: Request): string | undefined;
|
|
193
|
-
/**
|
|
194
|
-
* Get client IP address from request headers
|
|
195
|
-
*/
|
|
196
|
-
getClientIpAddress(request: Request): string | undefined;
|
|
197
|
-
/**
|
|
198
|
-
* Convert event to Facebook CAPI format with optional PII hashing
|
|
199
|
-
* Note: This method is now async to support SHA-256 IP hashing
|
|
200
|
-
*/
|
|
201
|
-
convertToCapiEvent(eventName: string, eventId: string, timestamp: number | undefined, enhancedParams: any, userInfo: any): Promise<CAPIEvent>;
|
|
202
|
-
/**
|
|
203
|
-
* Send event to Facebook CAPI with retry logic
|
|
204
|
-
*/
|
|
205
|
-
sendToFacebook(config: FacebookCAPIConfig, capiEvent: CAPIEvent): Promise<{
|
|
206
|
-
success: boolean;
|
|
207
|
-
data?: any;
|
|
208
|
-
error?: string;
|
|
209
|
-
pixelId: string;
|
|
210
|
-
}>;
|
|
211
|
-
/**
|
|
212
|
-
* Validate request body
|
|
213
|
-
*/
|
|
214
|
-
validateRequestBody(eventName: string, enhancedParams: any, bodySize: number): {
|
|
215
|
-
valid: boolean;
|
|
216
|
-
error?: string;
|
|
217
|
-
};
|
|
218
|
-
}
|
|
219
|
-
/**
|
|
220
|
-
* Singleton instance of the service
|
|
221
|
-
*/
|
|
222
|
-
declare const facebookCAPIService: FacebookCAPIService;
|
|
223
|
-
|
|
224
105
|
/**
|
|
225
106
|
* Component to initialize Shopify Analytics cookies
|
|
226
107
|
* This component handles the useShopifyCookies hook which must be used in a React component
|
|
@@ -230,4 +111,4 @@ interface ShopifyAnalyticsScriptProps {
|
|
|
230
111
|
}
|
|
231
112
|
declare function ShopifyAnalyticsScript({ domain, }: ShopifyAnalyticsScriptProps): null;
|
|
232
113
|
|
|
233
|
-
export { AffiliateConfig,
|
|
114
|
+
export { AffiliateConfig, GoogleAdapterConfig, LoggerConfig, MoengageAdapterConfig, MultiPixelAdapterConfig, PostHogAdapterConfig, ShopifyAdapterConfig, ShopifyAnalyticsScript, ShopkitAnalytics, type ShopkitAnalyticsConfig, type ShopkitAnalyticsProps, TrackingAdapter, ShopkitAnalytics as default };
|
package/dist/index.js
CHANGED
|
@@ -264,14 +264,12 @@ __export(src_exports, {
|
|
|
264
264
|
AffiliateTracker: () => AffiliateTracker_default,
|
|
265
265
|
BaseAdapter: () => BaseAdapter,
|
|
266
266
|
EventType: () => EventType,
|
|
267
|
-
FacebookCAPIService: () => FacebookCAPIService,
|
|
268
267
|
GoogleAdapter: () => GoogleAdapter,
|
|
269
268
|
MoengageAdapter: () => MoengageAdapter,
|
|
270
269
|
MultiPixelAdapter: () => MultiPixelAdapter,
|
|
271
270
|
PRIMA_EXPERIMENT_COOKIES: () => PRIMA_EXPERIMENT_COOKIES,
|
|
272
271
|
PRIMA_EXPERIMENT_COOKIE_NAMES: () => PRIMA_EXPERIMENT_COOKIE_NAMES,
|
|
273
272
|
PostHogAdapter: () => PostHogAdapter,
|
|
274
|
-
RateLimiter: () => RateLimiter,
|
|
275
273
|
ShopifyAdapter: () => ShopifyAdapter,
|
|
276
274
|
ShopifyAnalyticsScript: () => ShopifyAnalyticsScript,
|
|
277
275
|
ShopkitAnalytics: () => ShopkitAnalytics_default,
|
|
@@ -282,7 +280,6 @@ __export(src_exports, {
|
|
|
282
280
|
default: () => ShopkitAnalytics_default,
|
|
283
281
|
eventPublisher: () => eventPublisher,
|
|
284
282
|
eventSubscriber: () => eventSubscriber,
|
|
285
|
-
facebookCAPIService: () => facebookCAPIService,
|
|
286
283
|
generateEventId: () => generateEventId,
|
|
287
284
|
getAffiliateParams: () => getAffiliateParams,
|
|
288
285
|
getAffiliateSource: () => getAffiliateSource,
|
|
@@ -784,12 +781,13 @@ var MultiPixelAdapter = class extends BaseAdapter {
|
|
|
784
781
|
if (!eventName) {
|
|
785
782
|
return;
|
|
786
783
|
}
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
784
|
+
try {
|
|
785
|
+
window.fbq("track", eventName, enhancedParams, {
|
|
786
|
+
eventID: event.eventId
|
|
787
|
+
});
|
|
788
|
+
} catch (error) {
|
|
789
|
+
console.error("Facebook Pixel tracking error:", error);
|
|
790
|
+
}
|
|
793
791
|
}
|
|
794
792
|
/**
|
|
795
793
|
* Track event on server-side for all enabled pixels
|
|
@@ -809,14 +807,8 @@ var MultiPixelAdapter = class extends BaseAdapter {
|
|
|
809
807
|
eventId: event.eventId,
|
|
810
808
|
timestamp: event.timestamp,
|
|
811
809
|
enhancedParams,
|
|
812
|
-
userInfo: browserInfo
|
|
813
|
-
// Send all pixel info to the multi-pixel endpoint
|
|
814
|
-
pixels: this.pixels.filter((pixel) => pixel.config.enableCAPI).map((pixel) => ({
|
|
815
|
-
pixelId: pixel.config.pixelId,
|
|
816
|
-
name: pixel.config.name
|
|
817
|
-
}))
|
|
810
|
+
userInfo: browserInfo
|
|
818
811
|
};
|
|
819
|
-
console.log("@@@ trackServerSide", payload);
|
|
820
812
|
const endpoint = this.getConfig("capiEndpoint", "/api/events/multi");
|
|
821
813
|
const response = await fetch(endpoint, {
|
|
822
814
|
method: "POST",
|
|
@@ -826,7 +818,7 @@ var MultiPixelAdapter = class extends BaseAdapter {
|
|
|
826
818
|
body: JSON.stringify(payload)
|
|
827
819
|
});
|
|
828
820
|
if (!response.ok) {
|
|
829
|
-
|
|
821
|
+
this.logger.error(`Server-side tracking failed: ${response.status}`);
|
|
830
822
|
}
|
|
831
823
|
} catch (error) {
|
|
832
824
|
this.logger.error(
|
|
@@ -2433,332 +2425,17 @@ var ShopkitAnalytics = ({
|
|
|
2433
2425
|
] });
|
|
2434
2426
|
};
|
|
2435
2427
|
var ShopkitAnalytics_default = ShopkitAnalytics;
|
|
2436
|
-
|
|
2437
|
-
// src/utils/pii-hashing.ts
|
|
2438
|
-
async function hashString(value, normalize) {
|
|
2439
|
-
if (!value) {
|
|
2440
|
-
return void 0;
|
|
2441
|
-
}
|
|
2442
|
-
try {
|
|
2443
|
-
const normalizedValue = normalize ? normalize(value) : value;
|
|
2444
|
-
if (typeof crypto !== "undefined" && crypto.subtle) {
|
|
2445
|
-
const encoder = new TextEncoder();
|
|
2446
|
-
const data = encoder.encode(normalizedValue);
|
|
2447
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
2448
|
-
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
2449
|
-
const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2450
|
-
return hashHex;
|
|
2451
|
-
}
|
|
2452
|
-
} catch (error) {
|
|
2453
|
-
console.error("Error hashing with SHA-256:", error);
|
|
2454
|
-
}
|
|
2455
|
-
return fallbackHash(value);
|
|
2456
|
-
}
|
|
2457
|
-
async function hashIP(ip) {
|
|
2458
|
-
return hashString(ip, (value) => value.trim());
|
|
2459
|
-
}
|
|
2460
|
-
function fallbackHash(value) {
|
|
2461
|
-
let hash = 0;
|
|
2462
|
-
if (value.length === 0) {
|
|
2463
|
-
return "0";
|
|
2464
|
-
}
|
|
2465
|
-
for (let i = 0; i < value.length; i++) {
|
|
2466
|
-
const char = value.charCodeAt(i);
|
|
2467
|
-
hash = (hash << 5) - hash + char;
|
|
2468
|
-
hash = hash & hash;
|
|
2469
|
-
}
|
|
2470
|
-
return Math.abs(hash).toString(16).padStart(16, "0");
|
|
2471
|
-
}
|
|
2472
|
-
|
|
2473
|
-
// src/services/facebook-capi.service.ts
|
|
2474
|
-
var RateLimiter = class {
|
|
2475
|
-
constructor(requestsPerMinute = 100) {
|
|
2476
|
-
this.store = /* @__PURE__ */ new Map();
|
|
2477
|
-
this.limit = requestsPerMinute;
|
|
2478
|
-
this.window = 60 * 1e3;
|
|
2479
|
-
}
|
|
2480
|
-
check(ip) {
|
|
2481
|
-
const now = Date.now();
|
|
2482
|
-
let entry = this.store.get(ip);
|
|
2483
|
-
if (entry && entry.resetTime < now) {
|
|
2484
|
-
this.store.delete(ip);
|
|
2485
|
-
entry = void 0;
|
|
2486
|
-
}
|
|
2487
|
-
if (!entry) {
|
|
2488
|
-
entry = { count: 0, resetTime: now + this.window };
|
|
2489
|
-
this.store.set(ip, entry);
|
|
2490
|
-
}
|
|
2491
|
-
const allowed = entry.count < this.limit;
|
|
2492
|
-
entry.count++;
|
|
2493
|
-
return {
|
|
2494
|
-
allowed,
|
|
2495
|
-
remaining: Math.max(0, this.limit - entry.count)
|
|
2496
|
-
};
|
|
2497
|
-
}
|
|
2498
|
-
};
|
|
2499
|
-
var FacebookCAPIService = class {
|
|
2500
|
-
constructor() {
|
|
2501
|
-
this.rateLimiter = new RateLimiter(100);
|
|
2502
|
-
// 100 requests per minute per IP
|
|
2503
|
-
this.maxRetries = 3;
|
|
2504
|
-
this.baseDelay = 1e3;
|
|
2505
|
-
// 1 second
|
|
2506
|
-
this.timeout = 3e4;
|
|
2507
|
-
// 30 seconds
|
|
2508
|
-
this.debug = false;
|
|
2509
|
-
}
|
|
2510
|
-
// Override in consuming projects if needed
|
|
2511
|
-
/**
|
|
2512
|
-
* Check if request is within rate limits
|
|
2513
|
-
*/
|
|
2514
|
-
checkRateLimit(clientIp) {
|
|
2515
|
-
return this.rateLimiter.check(clientIp);
|
|
2516
|
-
}
|
|
2517
|
-
/**
|
|
2518
|
-
* Generate a unique event ID for deduplication
|
|
2519
|
-
*/
|
|
2520
|
-
generateEventId(eventName) {
|
|
2521
|
-
const timestamp = Date.now();
|
|
2522
|
-
const randomString = Math.random().toString(36).substring(2, 8);
|
|
2523
|
-
return `${timestamp}_${randomString}_${eventName}`;
|
|
2524
|
-
}
|
|
2525
|
-
/**
|
|
2526
|
-
* Extract fbc cookie from request headers
|
|
2527
|
-
*/
|
|
2528
|
-
extractFbcFromRequest(request) {
|
|
2529
|
-
const cookieHeader = request.headers.get("cookie");
|
|
2530
|
-
if (!cookieHeader) return void 0;
|
|
2531
|
-
const cookies = cookieHeader.split(";");
|
|
2532
|
-
for (const cookie of cookies) {
|
|
2533
|
-
const [name, value] = cookie.trim().split("=");
|
|
2534
|
-
if (name === "_fbc") {
|
|
2535
|
-
return value;
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2538
|
-
return void 0;
|
|
2539
|
-
}
|
|
2540
|
-
/**
|
|
2541
|
-
* Get client IP address from request headers
|
|
2542
|
-
*/
|
|
2543
|
-
getClientIpAddress(request) {
|
|
2544
|
-
const forwarded = request.headers.get("x-forwarded-for");
|
|
2545
|
-
const realIp = request.headers.get("x-real-ip");
|
|
2546
|
-
const cfConnectingIp = request.headers.get("cf-connecting-ip");
|
|
2547
|
-
return forwarded?.split(",")[0] || realIp || cfConnectingIp || void 0;
|
|
2548
|
-
}
|
|
2549
|
-
/**
|
|
2550
|
-
* Convert event to Facebook CAPI format with optional PII hashing
|
|
2551
|
-
* Note: This method is now async to support SHA-256 IP hashing
|
|
2552
|
-
*/
|
|
2553
|
-
async convertToCapiEvent(eventName, eventId, timestamp, enhancedParams, userInfo) {
|
|
2554
|
-
const eventTime = timestamp ? Math.floor(timestamp / 1e3) : Math.floor(Date.now() / 1e3);
|
|
2555
|
-
let hashedIp;
|
|
2556
|
-
if (userInfo?.clientIpAddress) {
|
|
2557
|
-
try {
|
|
2558
|
-
hashedIp = await hashIP(userInfo.clientIpAddress);
|
|
2559
|
-
} catch (error) {
|
|
2560
|
-
console.error("[Facebook CAPI] Error hashing IP:", error);
|
|
2561
|
-
hashedIp = userInfo.clientIpAddress;
|
|
2562
|
-
}
|
|
2563
|
-
}
|
|
2564
|
-
if (this.debug) {
|
|
2565
|
-
console.log("[Facebook CAPI] Event Conversion", {
|
|
2566
|
-
eventName,
|
|
2567
|
-
eventId,
|
|
2568
|
-
eventTime,
|
|
2569
|
-
timestamp_input: timestamp,
|
|
2570
|
-
fbp: !!userInfo?.fbp,
|
|
2571
|
-
fbc: !!userInfo?.fbc,
|
|
2572
|
-
ip_hashed: !!hashedIp,
|
|
2573
|
-
pii_fields: {
|
|
2574
|
-
em: !!userInfo?.em,
|
|
2575
|
-
ph: !!userInfo?.ph,
|
|
2576
|
-
fn: !!userInfo?.fn,
|
|
2577
|
-
ln: !!userInfo?.ln
|
|
2578
|
-
}
|
|
2579
|
-
});
|
|
2580
|
-
}
|
|
2581
|
-
const capiEvent = {
|
|
2582
|
-
event_name: eventName,
|
|
2583
|
-
event_time: eventTime,
|
|
2584
|
-
event_id: eventId,
|
|
2585
|
-
action_source: "website",
|
|
2586
|
-
user_data: {
|
|
2587
|
-
client_ip_address: hashedIp,
|
|
2588
|
-
client_user_agent: userInfo?.clientUserAgent,
|
|
2589
|
-
fbc: userInfo?.fbc,
|
|
2590
|
-
fbp: userInfo?.fbp
|
|
2591
|
-
},
|
|
2592
|
-
custom_data: enhancedParams
|
|
2593
|
-
};
|
|
2594
|
-
if (userInfo?.em) {
|
|
2595
|
-
capiEvent.user_data.em = userInfo.em;
|
|
2596
|
-
}
|
|
2597
|
-
if (userInfo?.ph) {
|
|
2598
|
-
capiEvent.user_data.ph = userInfo.ph;
|
|
2599
|
-
}
|
|
2600
|
-
if (userInfo?.fn) {
|
|
2601
|
-
capiEvent.user_data.fn = userInfo.fn;
|
|
2602
|
-
}
|
|
2603
|
-
if (userInfo?.ln) {
|
|
2604
|
-
capiEvent.user_data.ln = userInfo.ln;
|
|
2605
|
-
}
|
|
2606
|
-
if (userInfo?.country) {
|
|
2607
|
-
capiEvent.user_data.country = userInfo.country;
|
|
2608
|
-
}
|
|
2609
|
-
if (userInfo?.st) {
|
|
2610
|
-
capiEvent.user_data.st = userInfo.st;
|
|
2611
|
-
}
|
|
2612
|
-
if (userInfo?.zp) {
|
|
2613
|
-
capiEvent.user_data.zp = userInfo.zp;
|
|
2614
|
-
}
|
|
2615
|
-
if (userInfo?.db) {
|
|
2616
|
-
capiEvent.user_data.db = userInfo.db;
|
|
2617
|
-
}
|
|
2618
|
-
if (userInfo?.external_id) {
|
|
2619
|
-
capiEvent.user_data.external_id = userInfo.external_id;
|
|
2620
|
-
}
|
|
2621
|
-
return capiEvent;
|
|
2622
|
-
}
|
|
2623
|
-
/**
|
|
2624
|
-
* Send event to Facebook CAPI with retry logic
|
|
2625
|
-
*/
|
|
2626
|
-
async sendToFacebook(config, capiEvent) {
|
|
2627
|
-
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
2628
|
-
try {
|
|
2629
|
-
const payload = {
|
|
2630
|
-
data: [capiEvent],
|
|
2631
|
-
test_event_code: config.testEventCode
|
|
2632
|
-
};
|
|
2633
|
-
if (this.debug) {
|
|
2634
|
-
console.log("[Facebook CAPI] Sending to Facebook", {
|
|
2635
|
-
pixelId: config.pixelId,
|
|
2636
|
-
eventId: capiEvent.event_id,
|
|
2637
|
-
eventName: capiEvent.event_name,
|
|
2638
|
-
attempt: attempt + 1
|
|
2639
|
-
});
|
|
2640
|
-
}
|
|
2641
|
-
const url = `${config.baseUrl}/${config.pixelId}/events?access_token=${config.accessToken}`;
|
|
2642
|
-
const controller = new AbortController();
|
|
2643
|
-
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
2644
|
-
try {
|
|
2645
|
-
const response = await fetch(url, {
|
|
2646
|
-
method: "POST",
|
|
2647
|
-
headers: {
|
|
2648
|
-
"Content-Type": "application/json"
|
|
2649
|
-
},
|
|
2650
|
-
body: JSON.stringify(payload),
|
|
2651
|
-
signal: controller.signal
|
|
2652
|
-
});
|
|
2653
|
-
clearTimeout(timeoutId);
|
|
2654
|
-
if (!response.ok) {
|
|
2655
|
-
if ((response.status >= 500 || response.status === 429) && attempt < this.maxRetries) {
|
|
2656
|
-
const delay = this.baseDelay * Math.pow(2, attempt);
|
|
2657
|
-
if (this.debug) {
|
|
2658
|
-
console.log(`[Facebook CAPI] Retrying in ${delay}ms`, {
|
|
2659
|
-
pixelId: config.pixelId,
|
|
2660
|
-
status: response.status
|
|
2661
|
-
});
|
|
2662
|
-
}
|
|
2663
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2664
|
-
continue;
|
|
2665
|
-
}
|
|
2666
|
-
console.error(
|
|
2667
|
-
`Facebook CAPI Error for pixel ${config.pixelId} (attempt ${attempt + 1}/${this.maxRetries + 1}):`,
|
|
2668
|
-
{ status: response.status }
|
|
2669
|
-
);
|
|
2670
|
-
return {
|
|
2671
|
-
success: false,
|
|
2672
|
-
error: `Facebook CAPI request failed: ${response.status}`,
|
|
2673
|
-
pixelId: config.pixelId
|
|
2674
|
-
};
|
|
2675
|
-
}
|
|
2676
|
-
const result = await response.json();
|
|
2677
|
-
if (this.debug) {
|
|
2678
|
-
console.log(
|
|
2679
|
-
`Facebook CAPI: Events sent successfully to pixel ${config.pixelId}`,
|
|
2680
|
-
{
|
|
2681
|
-
events_received: result.events_received
|
|
2682
|
-
}
|
|
2683
|
-
);
|
|
2684
|
-
}
|
|
2685
|
-
return {
|
|
2686
|
-
success: true,
|
|
2687
|
-
data: result,
|
|
2688
|
-
pixelId: config.pixelId
|
|
2689
|
-
};
|
|
2690
|
-
} finally {
|
|
2691
|
-
clearTimeout(timeoutId);
|
|
2692
|
-
}
|
|
2693
|
-
} catch (error) {
|
|
2694
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
2695
|
-
if (attempt < this.maxRetries) {
|
|
2696
|
-
const delay = this.baseDelay * Math.pow(2, attempt);
|
|
2697
|
-
if (this.debug) {
|
|
2698
|
-
console.log(`[Facebook CAPI] Retrying in ${delay}ms after error`, {
|
|
2699
|
-
pixelId: config.pixelId,
|
|
2700
|
-
error: errorMessage
|
|
2701
|
-
});
|
|
2702
|
-
}
|
|
2703
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
2704
|
-
continue;
|
|
2705
|
-
}
|
|
2706
|
-
console.error(
|
|
2707
|
-
`Facebook CAPI Network Error for pixel ${config.pixelId} (attempt ${attempt + 1}/${this.maxRetries + 1}):`,
|
|
2708
|
-
{ error: errorMessage }
|
|
2709
|
-
);
|
|
2710
|
-
return {
|
|
2711
|
-
success: false,
|
|
2712
|
-
error: `Network error: ${errorMessage}`,
|
|
2713
|
-
pixelId: config.pixelId
|
|
2714
|
-
};
|
|
2715
|
-
}
|
|
2716
|
-
}
|
|
2717
|
-
return {
|
|
2718
|
-
success: false,
|
|
2719
|
-
error: "Failed after all retry attempts",
|
|
2720
|
-
pixelId: config.pixelId
|
|
2721
|
-
};
|
|
2722
|
-
}
|
|
2723
|
-
/**
|
|
2724
|
-
* Validate request body
|
|
2725
|
-
*/
|
|
2726
|
-
validateRequestBody(eventName, enhancedParams, bodySize) {
|
|
2727
|
-
if (!eventName || !enhancedParams) {
|
|
2728
|
-
return {
|
|
2729
|
-
valid: false,
|
|
2730
|
-
error: "eventName and enhancedParams are required"
|
|
2731
|
-
};
|
|
2732
|
-
}
|
|
2733
|
-
if (bodySize > 1024 * 1024) {
|
|
2734
|
-
return {
|
|
2735
|
-
valid: false,
|
|
2736
|
-
error: "Request body too large (max 1MB)"
|
|
2737
|
-
};
|
|
2738
|
-
}
|
|
2739
|
-
if (!/^[a-zA-Z0-9_]+$/.test(eventName)) {
|
|
2740
|
-
return {
|
|
2741
|
-
valid: false,
|
|
2742
|
-
error: "eventName must contain only alphanumeric characters and underscores"
|
|
2743
|
-
};
|
|
2744
|
-
}
|
|
2745
|
-
return { valid: true };
|
|
2746
|
-
}
|
|
2747
|
-
};
|
|
2748
|
-
var facebookCAPIService = new FacebookCAPIService();
|
|
2749
2428
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2750
2429
|
0 && (module.exports = {
|
|
2751
2430
|
AffiliateTracker,
|
|
2752
2431
|
BaseAdapter,
|
|
2753
2432
|
EventType,
|
|
2754
|
-
FacebookCAPIService,
|
|
2755
2433
|
GoogleAdapter,
|
|
2756
2434
|
MoengageAdapter,
|
|
2757
2435
|
MultiPixelAdapter,
|
|
2758
2436
|
PRIMA_EXPERIMENT_COOKIES,
|
|
2759
2437
|
PRIMA_EXPERIMENT_COOKIE_NAMES,
|
|
2760
2438
|
PostHogAdapter,
|
|
2761
|
-
RateLimiter,
|
|
2762
2439
|
ShopifyAdapter,
|
|
2763
2440
|
ShopifyAnalyticsScript,
|
|
2764
2441
|
ShopkitAnalytics,
|
|
@@ -2768,7 +2445,6 @@ var facebookCAPIService = new FacebookCAPIService();
|
|
|
2768
2445
|
createLogger,
|
|
2769
2446
|
eventPublisher,
|
|
2770
2447
|
eventSubscriber,
|
|
2771
|
-
facebookCAPIService,
|
|
2772
2448
|
generateEventId,
|
|
2773
2449
|
getAffiliateParams,
|
|
2774
2450
|
getAffiliateSource,
|