@amboras-dev/snapchat-ads 2.0.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.
@@ -0,0 +1,657 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/slot-components/index.ts
22
+ var slot_components_exports = {};
23
+ __export(slot_components_exports, {
24
+ SnapAddCart: () => SnapAddCart,
25
+ SnapPixelProvider: () => SnapPixelProvider,
26
+ SnapPurchase: () => SnapPurchase,
27
+ SnapStartCheckout: () => SnapStartCheckout,
28
+ SnapViewContent: () => SnapViewContent
29
+ });
30
+ module.exports = __toCommonJS(slot_components_exports);
31
+
32
+ // src/slot-components/SnapPixelProvider.tsx
33
+ var import_react = require("react");
34
+ var import_navigation = require("next/navigation");
35
+
36
+ // src/lib/consent.ts
37
+ function hasAnalyticsConsent() {
38
+ if (typeof document === "undefined") return false;
39
+ const match = document.cookie.match(/(?:^|;\s*)amboras_consent=([^;]*)/);
40
+ if (!match) return true;
41
+ try {
42
+ return JSON.parse(decodeURIComponent(match[1]))?.analytics === true;
43
+ } catch {
44
+ return false;
45
+ }
46
+ }
47
+
48
+ // src/lib/snap-pixel-loader.ts
49
+ var SCRIPT_SRC = "https://sc-static.net/scevent.min.js";
50
+ function installSnapPixelShim() {
51
+ if (typeof window === "undefined" || typeof document === "undefined") return;
52
+ if (window.snaptr) return;
53
+ const queue = [];
54
+ const shim = function(...args) {
55
+ if (typeof shim.handleRequest === "function") {
56
+ shim.handleRequest(...args);
57
+ return;
58
+ }
59
+ queue.push(args);
60
+ };
61
+ shim.push = function(...args) {
62
+ queue.push(args);
63
+ };
64
+ shim.queue = queue;
65
+ shim.version = "1.0";
66
+ shim.loaded = true;
67
+ window.snaptr = shim;
68
+ const script = document.createElement("script");
69
+ script.async = true;
70
+ script.src = SCRIPT_SRC;
71
+ document.head.appendChild(script);
72
+ }
73
+ function safeSnaptr(...args) {
74
+ if (typeof window === "undefined") return false;
75
+ const fn = window.snaptr;
76
+ if (typeof fn !== "function") return false;
77
+ try {
78
+ fn(...args);
79
+ return true;
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ // src/lib/dedup.ts
86
+ var MS_PER_MINUTE = 60 * 1e3;
87
+ function deriveMinuteBucket(nowMs) {
88
+ if (!Number.isFinite(nowMs)) return 0;
89
+ return Math.floor(nowMs / MS_PER_MINUTE);
90
+ }
91
+ function minuteBucketNow() {
92
+ return deriveMinuteBucket(Date.now());
93
+ }
94
+ function safeKey(primaryKey) {
95
+ if (!primaryKey) return "unknown";
96
+ return primaryKey.replace(/[^a-zA-Z0-9_-]/g, "_");
97
+ }
98
+ function makeEventId(eventName, primaryKey, minuteBucket = minuteBucketNow()) {
99
+ return `${eventName}_${safeKey(primaryKey)}_${minuteBucket}`;
100
+ }
101
+ function deriveOrderIdForPurchase(eventName, orderId) {
102
+ if (eventName !== "PURCHASE") return void 0;
103
+ if (typeof orderId !== "string") return void 0;
104
+ const trimmed = orderId.trim();
105
+ return trimmed ? trimmed : void 0;
106
+ }
107
+
108
+ // src/slot-components/SnapPixelProvider.tsx
109
+ function SnapPixelProvider({ pixelId }) {
110
+ const [consented, setConsented] = (0, import_react.useState)(false);
111
+ const initedForPixelId = (0, import_react.useRef)(null);
112
+ const pathname = (0, import_navigation.usePathname)();
113
+ const searchParams = (0, import_navigation.useSearchParams)();
114
+ (0, import_react.useEffect)(() => {
115
+ setConsented(hasAnalyticsConsent());
116
+ const handler = () => setConsented(hasAnalyticsConsent());
117
+ window.addEventListener("amboras-consent-changed", handler);
118
+ return () => window.removeEventListener("amboras-consent-changed", handler);
119
+ }, []);
120
+ (0, import_react.useEffect)(() => {
121
+ if (!pixelId || !consented) return;
122
+ installSnapPixelShim();
123
+ if (initedForPixelId.current !== pixelId) {
124
+ safeSnaptr("init", pixelId);
125
+ initedForPixelId.current = pixelId;
126
+ }
127
+ }, [pixelId, consented]);
128
+ (0, import_react.useEffect)(() => {
129
+ if (!pixelId || !consented) return;
130
+ if (initedForPixelId.current === null) return;
131
+ const key = `${pathname ?? "/"}?${searchParams?.toString() ?? ""}`;
132
+ const eid = makeEventId("PAGE_VIEW", key, minuteBucketNow());
133
+ safeSnaptr("track", "PAGE_VIEW", { client_dedup_id: eid });
134
+ }, [pathname, searchParams, pixelId, consented]);
135
+ return null;
136
+ }
137
+
138
+ // src/slot-components/SnapViewContent.tsx
139
+ var import_react2 = require("react");
140
+
141
+ // src/hooks/useSnapchatEvents.ts
142
+ var CAPI_PROXY_ENDPOINT = "/api/snapchat-ads/events";
143
+ function currentUrl() {
144
+ return typeof window !== "undefined" ? window.location.href : "";
145
+ }
146
+ async function sendSnapchatCapiEvent(payload) {
147
+ try {
148
+ await fetch(CAPI_PROXY_ENDPOINT, {
149
+ method: "POST",
150
+ headers: { "Content-Type": "application/json" },
151
+ body: JSON.stringify(payload)
152
+ });
153
+ } catch {
154
+ }
155
+ }
156
+ function trackViewContent(productId, options = {}) {
157
+ const eid = options.eventId ?? makeEventId("VIEW_CONTENT", productId, minuteBucketNow());
158
+ const contentIds = options.contentIds ?? [productId];
159
+ const customData = {
160
+ ...options.currency ? { currency: options.currency } : {},
161
+ ...options.value !== void 0 ? { value: options.value } : {},
162
+ ...contentIds.length ? { contentIds } : {},
163
+ ...options.contents?.length ? { contents: options.contents } : {},
164
+ ...typeof options.numItems === "number" ? { numItems: options.numItems } : {}
165
+ };
166
+ safeSnaptr("track", "VIEW_CONTENT", {
167
+ ...contentIds.length ? { item_ids: contentIds } : {},
168
+ ...options.currency ? { currency: options.currency } : {},
169
+ ...options.value !== void 0 ? { value: options.value } : {},
170
+ client_dedup_id: eid
171
+ });
172
+ void sendSnapchatCapiEvent({
173
+ eventName: "VIEW_CONTENT",
174
+ eventId: eid,
175
+ eventSourceUrl: currentUrl(),
176
+ userData: options.userData ?? {},
177
+ customData
178
+ });
179
+ }
180
+ function trackAddCart(cartId, options = {}) {
181
+ const eid = options.eventId ?? makeEventId("ADD_CART", cartId, minuteBucketNow());
182
+ const customData = {
183
+ ...options.currency ? { currency: options.currency } : {},
184
+ ...options.value !== void 0 ? { value: options.value } : {},
185
+ ...options.contentIds?.length ? { contentIds: options.contentIds } : {},
186
+ ...options.contents?.length ? { contents: options.contents } : {},
187
+ ...typeof options.numItems === "number" ? { numItems: options.numItems } : {}
188
+ };
189
+ safeSnaptr("track", "ADD_CART", {
190
+ ...options.contentIds?.length ? { item_ids: options.contentIds } : {},
191
+ ...options.currency ? { currency: options.currency } : {},
192
+ ...options.value !== void 0 ? { value: options.value } : {},
193
+ ...typeof options.numItems === "number" ? { number_items: options.numItems } : {},
194
+ client_dedup_id: eid
195
+ });
196
+ void sendSnapchatCapiEvent({
197
+ eventName: "ADD_CART",
198
+ eventId: eid,
199
+ eventSourceUrl: currentUrl(),
200
+ userData: options.userData ?? {},
201
+ customData
202
+ });
203
+ }
204
+ function trackStartCheckout(cartId, options = {}) {
205
+ const eid = options.eventId ?? makeEventId("START_CHECKOUT", cartId, minuteBucketNow());
206
+ const customData = {
207
+ ...options.currency ? { currency: options.currency } : {},
208
+ ...options.value !== void 0 ? { value: options.value } : {},
209
+ ...options.contentIds?.length ? { contentIds: options.contentIds } : {},
210
+ ...options.contents?.length ? { contents: options.contents } : {},
211
+ ...typeof options.numItems === "number" ? { numItems: options.numItems } : {}
212
+ };
213
+ safeSnaptr("track", "START_CHECKOUT", {
214
+ ...options.contentIds?.length ? { item_ids: options.contentIds } : {},
215
+ ...options.currency ? { currency: options.currency } : {},
216
+ ...options.value !== void 0 ? { value: options.value } : {},
217
+ ...typeof options.numItems === "number" ? { number_items: options.numItems } : {},
218
+ client_dedup_id: eid
219
+ });
220
+ void sendSnapchatCapiEvent({
221
+ eventName: "START_CHECKOUT",
222
+ eventId: eid,
223
+ eventSourceUrl: currentUrl(),
224
+ userData: options.userData ?? {},
225
+ customData
226
+ });
227
+ }
228
+ function trackPurchase(orderId, options = {}) {
229
+ const eid = options.eventId ?? makeEventId("PURCHASE", orderId, 0);
230
+ const derivedOrderId = deriveOrderIdForPurchase("PURCHASE", orderId);
231
+ const customData = {
232
+ ...options.currency ? { currency: options.currency } : {},
233
+ ...options.value !== void 0 ? { value: options.value } : {},
234
+ ...options.contentIds?.length ? { contentIds: options.contentIds } : {},
235
+ ...options.contents?.length ? { contents: options.contents } : {},
236
+ ...typeof options.numItems === "number" ? { numItems: options.numItems } : {},
237
+ ...derivedOrderId ? { orderId: derivedOrderId } : {}
238
+ };
239
+ safeSnaptr("track", "PURCHASE", {
240
+ ...options.contentIds?.length ? { item_ids: options.contentIds } : {},
241
+ ...options.currency ? { currency: options.currency } : {},
242
+ ...options.value !== void 0 ? { value: options.value } : {},
243
+ ...typeof options.numItems === "number" ? { number_items: options.numItems } : {},
244
+ ...derivedOrderId ? { transaction_id: derivedOrderId } : {},
245
+ client_dedup_id: eid
246
+ });
247
+ void sendSnapchatCapiEvent({
248
+ eventName: "PURCHASE",
249
+ eventId: eid,
250
+ eventSourceUrl: currentUrl(),
251
+ userData: options.userData ?? {},
252
+ customData
253
+ });
254
+ }
255
+
256
+ // src/lib/currency.ts
257
+ var ZERO_DECIMAL = [
258
+ "BIF",
259
+ "CLP",
260
+ "DJF",
261
+ "GNF",
262
+ "ISK",
263
+ "JPY",
264
+ "KMF",
265
+ "KRW",
266
+ "PYG",
267
+ "RWF",
268
+ "UGX",
269
+ "UYI",
270
+ "VND",
271
+ "VUV",
272
+ "XAF",
273
+ "XOF",
274
+ "XPF"
275
+ ];
276
+ var THREE_DECIMAL = ["BHD", "IQD", "JOD", "KWD", "LYD", "OMR", "TND"];
277
+ function decimalsForCurrency(currency) {
278
+ if (!currency) return 2;
279
+ const code = currency.toUpperCase();
280
+ if (ZERO_DECIMAL.includes(code)) return 0;
281
+ if (THREE_DECIMAL.includes(code)) return 3;
282
+ return 2;
283
+ }
284
+ function toMajorUnits(minorAmount, currency) {
285
+ if (typeof minorAmount !== "number" || !Number.isFinite(minorAmount)) return void 0;
286
+ const decimals = decimalsForCurrency(currency);
287
+ if (decimals === 0) return minorAmount;
288
+ const factor = 10 ** decimals;
289
+ return minorAmount / factor;
290
+ }
291
+ function toSnapValue(majorAmount, currency) {
292
+ if (typeof majorAmount !== "number" || !Number.isFinite(majorAmount)) return void 0;
293
+ const decimals = decimalsForCurrency(currency);
294
+ return majorAmount.toFixed(decimals);
295
+ }
296
+ function normalizeCurrency(currency) {
297
+ if (!currency) return void 0;
298
+ const trimmed = currency.trim();
299
+ if (!trimmed) return void 0;
300
+ return trimmed.toUpperCase();
301
+ }
302
+ function toSnapQuantity(quantity) {
303
+ if (typeof quantity !== "number" || !Number.isFinite(quantity)) return void 0;
304
+ return String(Math.trunc(quantity));
305
+ }
306
+
307
+ // src/slot-components/SnapViewContent.tsx
308
+ function SnapViewContent({ productId, price, currency }) {
309
+ const lastProductId = (0, import_react2.useRef)(null);
310
+ (0, import_react2.useEffect)(() => {
311
+ if (!productId || lastProductId.current === productId) return;
312
+ lastProductId.current = productId;
313
+ const normalizedCurrency = normalizeCurrency(currency);
314
+ const value = toSnapValue(price, normalizedCurrency);
315
+ trackViewContent(productId, {
316
+ currency: normalizedCurrency,
317
+ value,
318
+ contentIds: [productId],
319
+ numItems: 1
320
+ });
321
+ }, [productId, price, currency]);
322
+ return null;
323
+ }
324
+
325
+ // src/slot-components/SnapAddCart.tsx
326
+ var import_react3 = require("react");
327
+
328
+ // src/lib/backend.ts
329
+ function resolveBackendUrl() {
330
+ if (typeof process === "undefined" || !process.env) return "";
331
+ return process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL ?? process.env.NEXT_PUBLIC_API_URL ?? process.env.VITE_MEDUSA_BACKEND_URL ?? "";
332
+ }
333
+ function resolvePublishableKey() {
334
+ if (typeof process === "undefined" || !process.env) return "";
335
+ return process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY ?? process.env.NEXT_PUBLIC_PUBLISHABLE_KEY ?? process.env.VITE_MEDUSA_PUBLISHABLE_KEY ?? "";
336
+ }
337
+ function resolveStoreEnvironmentId() {
338
+ if (typeof process === "undefined" || !process.env) return "";
339
+ return process.env.NEXT_PUBLIC_STORE_ID ?? "";
340
+ }
341
+ function storeHeaders() {
342
+ const key = resolvePublishableKey();
343
+ const storeId = resolveStoreEnvironmentId();
344
+ const headers = {};
345
+ if (key) headers["x-publishable-api-key"] = key;
346
+ if (storeId) headers["X-Store-Environment-ID"] = storeId;
347
+ return headers;
348
+ }
349
+
350
+ // src/slot-components/SnapAddCart.tsx
351
+ var STORAGE_KEY = "amboras_snap_cart_state_v1";
352
+ var DEDUPE_WINDOW_MS = 1500;
353
+ var RECENT_FIRES = (() => {
354
+ const KEY = "__amboras_snap_ads_recent_fires__";
355
+ const g = globalThis;
356
+ const existing = g[KEY];
357
+ if (existing instanceof Map) return existing;
358
+ const fresh = /* @__PURE__ */ new Map();
359
+ g[KEY] = fresh;
360
+ return fresh;
361
+ })();
362
+ function readStored() {
363
+ if (typeof window === "undefined") return null;
364
+ try {
365
+ const raw = window.sessionStorage.getItem(STORAGE_KEY);
366
+ return raw ? JSON.parse(raw) : null;
367
+ } catch {
368
+ return null;
369
+ }
370
+ }
371
+ function writeStored(snapshot) {
372
+ if (typeof window === "undefined") return;
373
+ try {
374
+ window.sessionStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot));
375
+ } catch {
376
+ }
377
+ }
378
+ async function fetchCart(cartId) {
379
+ const baseUrl = resolveBackendUrl();
380
+ if (!baseUrl) return null;
381
+ try {
382
+ const res = await fetch(
383
+ `${baseUrl}/store/carts/${encodeURIComponent(cartId)}?fields=id,currency_code,*items`,
384
+ { method: "GET", headers: storeHeaders(), credentials: "include" }
385
+ );
386
+ if (!res.ok) return null;
387
+ const json = await res.json();
388
+ return json.cart ?? null;
389
+ } catch {
390
+ return null;
391
+ }
392
+ }
393
+ function withinDedupeWindow(variantId) {
394
+ const now = Date.now();
395
+ const last = RECENT_FIRES.get(variantId);
396
+ if (last && now - last < DEDUPE_WINDOW_MS) return true;
397
+ RECENT_FIRES.set(variantId, now);
398
+ if (RECENT_FIRES.size > 50) {
399
+ const cutoff = now - DEDUPE_WINDOW_MS * 4;
400
+ for (const [k, t] of RECENT_FIRES) {
401
+ if (t < cutoff) RECENT_FIRES.delete(k);
402
+ }
403
+ }
404
+ return false;
405
+ }
406
+ function SnapAddCart({ cartId, itemCount: _itemCount }) {
407
+ const previousCartId = (0, import_react3.useRef)(null);
408
+ (0, import_react3.useEffect)(() => {
409
+ if (!cartId) return;
410
+ let cancelled = false;
411
+ void (async () => {
412
+ const cart = await fetchCart(cartId);
413
+ if (cancelled) return;
414
+ if (!cart) {
415
+ previousCartId.current = cartId;
416
+ return;
417
+ }
418
+ const items = Array.isArray(cart.items) ? cart.items : [];
419
+ const currency = normalizeCurrency(cart.currency_code);
420
+ const currentMap = {};
421
+ const unitPriceByVariant = {};
422
+ const productIdByVariant = {};
423
+ for (const it of items) {
424
+ if (!it.variant_id) continue;
425
+ currentMap[it.variant_id] = (currentMap[it.variant_id] ?? 0) + (it.quantity ?? 0);
426
+ unitPriceByVariant[it.variant_id] = it.unit_price ?? void 0;
427
+ productIdByVariant[it.variant_id] = it.product_id ?? void 0;
428
+ }
429
+ const stored = readStored();
430
+ const isFirstHydration = previousCartId.current === null;
431
+ let baseline;
432
+ if (stored && stored.cartId === cartId) {
433
+ baseline = stored.quantitiesByVariant;
434
+ } else if (isFirstHydration) {
435
+ baseline = currentMap;
436
+ } else {
437
+ baseline = {};
438
+ }
439
+ previousCartId.current = cartId;
440
+ for (const [variantId, newQty] of Object.entries(currentMap)) {
441
+ const oldQty = baseline[variantId] ?? 0;
442
+ const delta = newQty - oldQty;
443
+ if (delta <= 0) continue;
444
+ if (withinDedupeWindow(variantId)) continue;
445
+ const productId = productIdByVariant[variantId];
446
+ const unitPriceMinor = unitPriceByVariant[variantId];
447
+ const contentIds = productId ? [productId, variantId] : [variantId];
448
+ const majorValue = typeof unitPriceMinor === "number" && Number.isFinite(unitPriceMinor) ? toMajorUnits(unitPriceMinor * delta, currency) : void 0;
449
+ const valueStr = toSnapValue(majorValue, currency);
450
+ const perItemMajor = typeof unitPriceMinor === "number" && Number.isFinite(unitPriceMinor) ? toMajorUnits(unitPriceMinor, currency) : void 0;
451
+ const contents = productId ? [
452
+ {
453
+ id: productId,
454
+ quantity: toSnapQuantity(delta) ?? String(delta),
455
+ ...perItemMajor !== void 0 ? { item_price: toSnapValue(perItemMajor, currency) } : {}
456
+ }
457
+ ] : [];
458
+ trackAddCart(cartId, {
459
+ currency,
460
+ value: valueStr,
461
+ contentIds,
462
+ contents,
463
+ numItems: delta
464
+ });
465
+ }
466
+ writeStored({ cartId, quantitiesByVariant: currentMap });
467
+ })();
468
+ return () => {
469
+ cancelled = true;
470
+ };
471
+ }, [cartId, _itemCount]);
472
+ return null;
473
+ }
474
+
475
+ // src/slot-components/SnapStartCheckout.tsx
476
+ var import_react4 = require("react");
477
+ async function fetchCart2(cartId) {
478
+ const baseUrl = resolveBackendUrl();
479
+ if (!baseUrl) return null;
480
+ try {
481
+ const res = await fetch(
482
+ `${baseUrl}/store/carts/${encodeURIComponent(cartId)}?fields=id,currency_code,total,*items`,
483
+ { method: "GET", headers: storeHeaders(), credentials: "include" }
484
+ );
485
+ if (!res.ok) return null;
486
+ const json = await res.json();
487
+ return json.cart ?? null;
488
+ } catch {
489
+ return null;
490
+ }
491
+ }
492
+ var STORAGE_KEY2 = "amboras_snap_start_checkout_seen";
493
+ function readSeen() {
494
+ if (typeof window === "undefined") return /* @__PURE__ */ new Set();
495
+ try {
496
+ const raw = window.sessionStorage.getItem(STORAGE_KEY2);
497
+ if (!raw) return /* @__PURE__ */ new Set();
498
+ const parsed = JSON.parse(raw);
499
+ return Array.isArray(parsed) ? new Set(parsed.filter((v) => typeof v === "string")) : /* @__PURE__ */ new Set();
500
+ } catch {
501
+ return /* @__PURE__ */ new Set();
502
+ }
503
+ }
504
+ function writeSeen(set) {
505
+ if (typeof window === "undefined") return;
506
+ try {
507
+ window.sessionStorage.setItem(STORAGE_KEY2, JSON.stringify(Array.from(set).slice(-50)));
508
+ } catch {
509
+ }
510
+ }
511
+ function SnapStartCheckout({ cartId, itemCount, total, currency }) {
512
+ const fired = (0, import_react4.useRef)(null);
513
+ (0, import_react4.useEffect)(() => {
514
+ if (!cartId) return;
515
+ if (fired.current === cartId) return;
516
+ const seen = readSeen();
517
+ if (seen.has(cartId)) {
518
+ fired.current = cartId;
519
+ return;
520
+ }
521
+ let cancelled = false;
522
+ void (async () => {
523
+ const cart = await fetchCart2(cartId);
524
+ if (cancelled) return;
525
+ let resolvedItems = [];
526
+ let resolvedTotal = total;
527
+ let resolvedCurrency = currency;
528
+ let resolvedNumItems = itemCount ?? 0;
529
+ if (cart) {
530
+ if (Array.isArray(cart.items)) resolvedItems = cart.items;
531
+ if (typeof cart.total === "number" && resolvedTotal === void 0) resolvedTotal = cart.total;
532
+ if (typeof cart.currency_code === "string" && !resolvedCurrency) resolvedCurrency = cart.currency_code;
533
+ resolvedNumItems = resolvedItems.reduce((sum, it) => sum + (it.quantity ?? 0), 0) || resolvedNumItems;
534
+ }
535
+ const normalizedCurrency = normalizeCurrency(resolvedCurrency);
536
+ const contentIds = resolvedItems.map((it) => it.product_id).filter((id) => typeof id === "string" && id.length > 0);
537
+ const contents = resolvedItems.filter((it) => it.product_id).map((it) => ({
538
+ id: it.product_id,
539
+ quantity: toSnapQuantity(it.quantity ?? 1) ?? "1",
540
+ ...typeof it.unit_price === "number" ? { item_price: toSnapValue(toMajorUnits(it.unit_price, normalizedCurrency), normalizedCurrency) } : {}
541
+ }));
542
+ const majorValue = toMajorUnits(resolvedTotal, normalizedCurrency);
543
+ const valueStr = toSnapValue(majorValue, normalizedCurrency);
544
+ if (resolvedNumItems <= 0 && contentIds.length === 0) return;
545
+ trackStartCheckout(cartId, {
546
+ currency: normalizedCurrency,
547
+ value: valueStr,
548
+ contentIds,
549
+ contents,
550
+ numItems: resolvedNumItems
551
+ });
552
+ fired.current = cartId;
553
+ seen.add(cartId);
554
+ writeSeen(seen);
555
+ })();
556
+ return () => {
557
+ cancelled = true;
558
+ };
559
+ }, [cartId, itemCount, total, currency]);
560
+ return null;
561
+ }
562
+
563
+ // src/slot-components/SnapPurchase.tsx
564
+ var import_react5 = require("react");
565
+ async function fetchOrder(orderId) {
566
+ const baseUrl = resolveBackendUrl();
567
+ if (!baseUrl) return null;
568
+ try {
569
+ const res = await fetch(
570
+ `${baseUrl}/store/orders/${encodeURIComponent(orderId)}?fields=id,total,currency_code,*items`,
571
+ { method: "GET", headers: storeHeaders(), credentials: "include" }
572
+ );
573
+ if (!res.ok) return null;
574
+ const json = await res.json();
575
+ return json.order ?? null;
576
+ } catch {
577
+ return null;
578
+ }
579
+ }
580
+ function SnapPurchase({ orderId, total, currency, itemCount: _itemCount }) {
581
+ const fired = (0, import_react5.useRef)(null);
582
+ (0, import_react5.useEffect)(() => {
583
+ if (!orderId) return;
584
+ if (fired.current === orderId) return;
585
+ const dedupeKey = `amboras_snap_purchase_${orderId}`;
586
+ try {
587
+ if (typeof sessionStorage !== "undefined" && sessionStorage.getItem(dedupeKey)) {
588
+ fired.current = orderId;
589
+ return;
590
+ }
591
+ } catch {
592
+ }
593
+ let cancelled = false;
594
+ void (async () => {
595
+ const order = await fetchOrder(orderId);
596
+ if (cancelled) return;
597
+ const fetchSucceeded = !!order && typeof order.total === "number" && !!order.currency_code;
598
+ let dispatched = false;
599
+ const eid = makeEventId("PURCHASE", orderId, 0);
600
+ if (fetchSucceeded && order) {
601
+ const normalizedCurrency = normalizeCurrency(order.currency_code);
602
+ const resolvedItems = Array.isArray(order.items) ? order.items : [];
603
+ const contentIds = resolvedItems.map((it) => it.product_id).filter((id) => typeof id === "string" && id.length > 0);
604
+ const contents = resolvedItems.filter((it) => it.product_id).map((it) => ({
605
+ id: it.product_id,
606
+ quantity: toSnapQuantity(it.quantity ?? 1) ?? "1",
607
+ ...typeof it.unit_price === "number" ? { item_price: toSnapValue(toMajorUnits(it.unit_price, normalizedCurrency), normalizedCurrency) } : {}
608
+ }));
609
+ const majorValue = toMajorUnits(order.total, normalizedCurrency);
610
+ const valueStr = toSnapValue(majorValue, normalizedCurrency);
611
+ if (valueStr !== void 0) {
612
+ trackPurchase(orderId, {
613
+ currency: normalizedCurrency,
614
+ value: valueStr,
615
+ contentIds,
616
+ contents,
617
+ numItems: resolvedItems.reduce((sum, it) => sum + (it.quantity ?? 0), 0),
618
+ eventId: eid
619
+ });
620
+ dispatched = true;
621
+ }
622
+ } else if (typeof total === "number" && !!currency) {
623
+ const normalizedCurrency = normalizeCurrency(currency);
624
+ if (process.env.NODE_ENV !== "production") {
625
+ console.warn(
626
+ "[snapchat-ads] SnapPurchase: order fetch failed, firing minimal browser PURCHASE from merchant-supplied total/currency. content_ids unavailable in this branch. If your CAPI subscriber is healthy the server event will provide full attribution."
627
+ );
628
+ }
629
+ trackPurchase(orderId, {
630
+ currency: normalizedCurrency,
631
+ value: toSnapValue(total, normalizedCurrency),
632
+ eventId: eid
633
+ });
634
+ dispatched = true;
635
+ }
636
+ if (!dispatched) return;
637
+ fired.current = orderId;
638
+ try {
639
+ if (typeof sessionStorage !== "undefined") sessionStorage.setItem(dedupeKey, "1");
640
+ } catch {
641
+ }
642
+ })();
643
+ return () => {
644
+ cancelled = true;
645
+ };
646
+ }, [orderId, total, currency]);
647
+ return null;
648
+ }
649
+ // Annotate the CommonJS export names for ESM import in node:
650
+ 0 && (module.exports = {
651
+ SnapAddCart,
652
+ SnapPixelProvider,
653
+ SnapPurchase,
654
+ SnapStartCheckout,
655
+ SnapViewContent
656
+ });
657
+ //# sourceMappingURL=index.js.map