@replohq/sdk 1.2.0 → 1.2.2

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.
Files changed (39) hide show
  1. package/analytics/hooks/use-product-viewed-analytics.d.ts +6 -1
  2. package/analytics/hooks/use-product-viewed-analytics.js +4 -1
  3. package/analytics/hooks/use-product-viewed-analytics.js.map +2 -2
  4. package/analytics/replo-pixel-script.d.ts +1 -1
  5. package/analytics/replo-pixel-script.js.map +1 -1
  6. package/analytics/sinks/converge-event-mapper.d.ts +41 -0
  7. package/analytics/sinks/converge-event-mapper.js +182 -0
  8. package/analytics/sinks/converge-event-mapper.js.map +7 -0
  9. package/analytics/sinks/converge-sink.d.ts +24 -9
  10. package/analytics/sinks/converge-sink.js +45 -23
  11. package/analytics/sinks/converge-sink.js.map +2 -2
  12. package/analytics/sinks/northbeam-sink.d.ts +2 -4
  13. package/analytics/sinks/northbeam-sink.js +1 -4
  14. package/analytics/sinks/northbeam-sink.js.map +2 -2
  15. package/analytics/utils/analytics-utils.d.ts +6 -1
  16. package/analytics/utils/analytics-utils.js +24 -11
  17. package/analytics/utils/analytics-utils.js.map +2 -2
  18. package/analytics/utils/navigation-tracker.d.ts +2 -0
  19. package/analytics/utils/navigation-tracker.js +7 -3
  20. package/analytics/utils/navigation-tracker.js.map +2 -2
  21. package/cart/cart-provider.js +4 -1
  22. package/cart/cart-provider.js.map +2 -2
  23. package/cart/cart-schema.d.ts +4 -0
  24. package/cart/cart-schema.js +6 -1
  25. package/cart/cart-schema.js.map +2 -2
  26. package/cart/cart-types.d.ts +12 -0
  27. package/cart/cart-types.js.map +1 -1
  28. package/cart/utils/cart-utils.d.ts +7 -6
  29. package/cart/utils/cart-utils.js +6 -3
  30. package/cart/utils/cart-utils.js.map +2 -2
  31. package/consent/inject-script-descriptors.d.ts +3 -3
  32. package/consent/inject-script-descriptors.js.map +1 -1
  33. package/consent/replo-scripts.d.ts +8 -2
  34. package/consent/replo-scripts.js +49 -33
  35. package/consent/replo-scripts.js.map +2 -2
  36. package/consent/script-snippets.js +2 -6
  37. package/consent/script-snippets.js.map +2 -2
  38. package/lib/buildMetadata.js +3 -3
  39. package/package.json +4 -4
@@ -8,7 +8,12 @@ interface AnalyticsProductVariant {
8
8
  id: string;
9
9
  title?: string;
10
10
  sku?: string;
11
- price?: number;
11
+ price?: number | {
12
+ amount: number | string;
13
+ currencyCode: string;
14
+ };
15
+ currency?: string;
16
+ priceCurrencyCode?: string | null;
12
17
  }
13
18
  interface UseProductViewedAnalyticsProps {
14
19
  product: AnalyticsProduct | null;
@@ -12,6 +12,8 @@ function useProductViewedAnalytics({
12
12
  const trackingKey = `${product.id}-${selectedVariant.id}`;
13
13
  if (lastTrackedRef.current !== trackingKey) {
14
14
  lastTrackedRef.current = trackingKey;
15
+ const price = typeof selectedVariant.price === "object" ? Number(selectedVariant.price.amount) : selectedVariant.price ?? 0;
16
+ const currency = typeof selectedVariant.price === "object" ? selectedVariant.price.currencyCode : selectedVariant.currency ?? selectedVariant.priceCurrencyCode ?? void 0;
15
17
  void analytics.productViewed({
16
18
  data: {
17
19
  productId: product.id,
@@ -21,7 +23,8 @@ function useProductViewedAnalytics({
21
23
  variantId: selectedVariant.id ?? "",
22
24
  variantTitle: selectedVariant.title ?? "",
23
25
  variantSku: selectedVariant.sku ?? "",
24
- price: selectedVariant.price ?? 0
26
+ price,
27
+ currency
25
28
  }
26
29
  });
27
30
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../analytics/hooks/use-product-viewed-analytics.ts"],
4
- "sourcesContent": ["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\nimport { useAnalytics } from \"../analytics-provider\";\n\ninterface AnalyticsProduct {\n id: string;\n title: string;\n slug?: string;\n description?: string;\n}\n\ninterface AnalyticsProductVariant {\n id: string;\n title?: string;\n sku?: string;\n price?: number;\n}\n\ninterface UseProductViewedAnalyticsProps {\n product: AnalyticsProduct | null;\n selectedVariant?: AnalyticsProductVariant | null;\n}\n\n/**\n * Fires a `productViewed` analytics event when the product or selected variant\n * changes. Deduplicates by product+variant ID so the same combination is only\n * tracked once per component lifecycle.\n */\nexport function useProductViewedAnalytics({\n product,\n selectedVariant,\n}: UseProductViewedAnalyticsProps) {\n const analytics = useAnalytics();\n const lastTrackedRef = useRef<string | null>(null);\n\n // eslint-disable-next-line replo/no-use-effect -- Legacy effect - may or may not be necessary\n useEffect(() => {\n if (analytics && product && selectedVariant) {\n const trackingKey = `${product.id}-${selectedVariant.id}`;\n\n if (lastTrackedRef.current !== trackingKey) {\n lastTrackedRef.current = trackingKey;\n\n void analytics.productViewed({\n data: {\n productId: product.id,\n productTitle: product.title,\n productSlug: product.slug,\n productDescription: product.description,\n variantId: selectedVariant.id ?? \"\",\n variantTitle: selectedVariant.title ?? \"\",\n variantSku: selectedVariant.sku ?? \"\",\n price: selectedVariant.price ?? 0,\n },\n });\n }\n }\n }, [analytics, product, selectedVariant]);\n}\n"],
5
- "mappings": ";AAEA,SAAS,WAAW,cAAc;AAElC,SAAS,oBAAoB;AA0BtB,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,YAAY,aAAa;AAC/B,QAAM,iBAAiB,OAAsB,IAAI;AAGjD,YAAU,MAAM;AACd,QAAI,aAAa,WAAW,iBAAiB;AAC3C,YAAM,cAAc,GAAG,QAAQ,EAAE,IAAI,gBAAgB,EAAE;AAEvD,UAAI,eAAe,YAAY,aAAa;AAC1C,uBAAe,UAAU;AAEzB,aAAK,UAAU,cAAc;AAAA,UAC3B,MAAM;AAAA,YACJ,WAAW,QAAQ;AAAA,YACnB,cAAc,QAAQ;AAAA,YACtB,aAAa,QAAQ;AAAA,YACrB,oBAAoB,QAAQ;AAAA,YAC5B,WAAW,gBAAgB,MAAM;AAAA,YACjC,cAAc,gBAAgB,SAAS;AAAA,YACvC,YAAY,gBAAgB,OAAO;AAAA,YACnC,OAAO,gBAAgB,SAAS;AAAA,UAClC;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,SAAS,eAAe,CAAC;AAC1C;",
4
+ "sourcesContent": ["\"use client\";\n\nimport { useEffect, useRef } from \"react\";\n\nimport { useAnalytics } from \"../analytics-provider\";\n\ninterface AnalyticsProduct {\n id: string;\n title: string;\n slug?: string;\n description?: string;\n}\n\ninterface AnalyticsProductVariant {\n id: string;\n title?: string;\n sku?: string;\n price?: number | { amount: number | string; currencyCode: string };\n currency?: string;\n priceCurrencyCode?: string | null;\n}\n\ninterface UseProductViewedAnalyticsProps {\n product: AnalyticsProduct | null;\n selectedVariant?: AnalyticsProductVariant | null;\n}\n\n/**\n * Fires a `productViewed` analytics event when the product or selected variant\n * changes. Deduplicates by product+variant ID so the same combination is only\n * tracked once per component lifecycle.\n */\nexport function useProductViewedAnalytics({\n product,\n selectedVariant,\n}: UseProductViewedAnalyticsProps) {\n const analytics = useAnalytics();\n const lastTrackedRef = useRef<string | null>(null);\n\n // eslint-disable-next-line replo/no-use-effect -- Legacy effect - may or may not be necessary\n useEffect(() => {\n if (analytics && product && selectedVariant) {\n const trackingKey = `${product.id}-${selectedVariant.id}`;\n\n if (lastTrackedRef.current !== trackingKey) {\n lastTrackedRef.current = trackingKey;\n\n const price =\n typeof selectedVariant.price === \"object\"\n ? Number(selectedVariant.price.amount)\n : selectedVariant.price ?? 0;\n const currency =\n typeof selectedVariant.price === \"object\"\n ? selectedVariant.price.currencyCode\n : selectedVariant.currency ??\n selectedVariant.priceCurrencyCode ??\n undefined;\n\n void analytics.productViewed({\n data: {\n productId: product.id,\n productTitle: product.title,\n productSlug: product.slug,\n productDescription: product.description,\n variantId: selectedVariant.id ?? \"\",\n variantTitle: selectedVariant.title ?? \"\",\n variantSku: selectedVariant.sku ?? \"\",\n price,\n currency,\n },\n });\n }\n }\n }, [analytics, product, selectedVariant]);\n}\n"],
5
+ "mappings": ";AAEA,SAAS,WAAW,cAAc;AAElC,SAAS,oBAAoB;AA4BtB,SAAS,0BAA0B;AAAA,EACxC;AAAA,EACA;AACF,GAAmC;AACjC,QAAM,YAAY,aAAa;AAC/B,QAAM,iBAAiB,OAAsB,IAAI;AAGjD,YAAU,MAAM;AACd,QAAI,aAAa,WAAW,iBAAiB;AAC3C,YAAM,cAAc,GAAG,QAAQ,EAAE,IAAI,gBAAgB,EAAE;AAEvD,UAAI,eAAe,YAAY,aAAa;AAC1C,uBAAe,UAAU;AAEzB,cAAM,QACJ,OAAO,gBAAgB,UAAU,WAC7B,OAAO,gBAAgB,MAAM,MAAM,IACnC,gBAAgB,SAAS;AAC/B,cAAM,WACJ,OAAO,gBAAgB,UAAU,WAC7B,gBAAgB,MAAM,eACtB,gBAAgB,YAChB,gBAAgB,qBAChB;AAEN,aAAK,UAAU,cAAc;AAAA,UAC3B,MAAM;AAAA,YACJ,WAAW,QAAQ;AAAA,YACnB,cAAc,QAAQ;AAAA,YACtB,aAAa,QAAQ;AAAA,YACrB,oBAAoB,QAAQ;AAAA,YAC5B,WAAW,gBAAgB,MAAM;AAAA,YACjC,cAAc,gBAAgB,SAAS;AAAA,YACvC,YAAY,gBAAgB,OAAO;AAAA,YACnC;AAAA,YACA;AAAA,UACF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF,GAAG,CAAC,WAAW,SAAS,eAAe,CAAC;AAC1C;",
6
6
  "names": []
7
7
  }
@@ -7,7 +7,7 @@
7
7
  * `firstPartyAllowed` gate that controls whether `ReploSink` is created at all.
8
8
  *
9
9
  * Under a consent platform the tag is rendered regardless and `InjectedScript`
10
- * stamps the blocking attributes at effect time, since delegation is not yet
10
+ * stamps the blocking attributes at mount time, since delegation is not yet
11
11
  * set here.
12
12
  */
13
13
  export declare function ReploPixelScript({ pixelUrl, apiUrl, projectId, }: {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../analytics/replo-pixel-script.tsx"],
4
- "sourcesContent": ["\"use client\";\n\nimport type { ScriptTagDescriptor } from \"schemas/generated/consent\";\n\nimport { findDelegatedPlatform } from \"../consent/consent-platform\";\nimport { isConsentAllowed, useConsent } from \"../consent/consent-store\";\nimport { InjectedScript } from \"../consent/replo-scripts\";\n\n// Stable injection id so re-renders and React Strict Mode double-invokes never\n// inject the pixel twice (the loader itself also guards via `__reploPixelLoaded`).\nconst PIXEL_SCRIPT_ID = \"replo:first-party-pixel\";\n\n/**\n * Consent-gated injector for Replo's first-party analytics pixel. The loaded\n * script sets `window.ReploPixel`, which the always-on `ReploSink` forwards\n * events into, so gating it on `analytics` consent keeps the first-party sink\n * consent-correct without the pixel loading unconditionally in `<head>` the way\n * the legacy scaffold does. `analytics` consent here matches the\n * `firstPartyAllowed` gate that controls whether `ReploSink` is created at all.\n *\n * Under a consent platform the tag is rendered regardless and `InjectedScript`\n * stamps the blocking attributes at effect time, since delegation is not yet\n * set here.\n */\nexport function ReploPixelScript({\n pixelUrl,\n apiUrl,\n projectId,\n}: {\n pixelUrl: string;\n apiUrl: string;\n projectId: string;\n}) {\n const consent = useConsent();\n if (\n !findDelegatedPlatform() &&\n !isConsentAllowed({ state: consent, requiredConsent: [\"analytics\"] })\n ) {\n return null;\n }\n\n const descriptors: ScriptTagDescriptor[] = [\n {\n kind: \"external\",\n src: `${pixelUrl}/latest.js`,\n attributes: {\n \"data-api-url\": apiUrl,\n \"data-project-id\": projectId,\n },\n },\n ];\n\n return (\n <InjectedScript\n baseId={PIXEL_SCRIPT_ID}\n descriptors={descriptors}\n requiredConsent={[\"analytics\"]}\n />\n );\n}\n"],
4
+ "sourcesContent": ["\"use client\";\n\nimport type { ScriptTagDescriptor } from \"schemas/generated/consent\";\n\nimport { findDelegatedPlatform } from \"../consent/consent-platform\";\nimport { isConsentAllowed, useConsent } from \"../consent/consent-store\";\nimport { InjectedScript } from \"../consent/replo-scripts\";\n\n// Stable injection id so re-renders and React Strict Mode double-invokes never\n// inject the pixel twice (the loader itself also guards via `__reploPixelLoaded`).\nconst PIXEL_SCRIPT_ID = \"replo:first-party-pixel\";\n\n/**\n * Consent-gated injector for Replo's first-party analytics pixel. The loaded\n * script sets `window.ReploPixel`, which the always-on `ReploSink` forwards\n * events into, so gating it on `analytics` consent keeps the first-party sink\n * consent-correct without the pixel loading unconditionally in `<head>` the way\n * the legacy scaffold does. `analytics` consent here matches the\n * `firstPartyAllowed` gate that controls whether `ReploSink` is created at all.\n *\n * Under a consent platform the tag is rendered regardless and `InjectedScript`\n * stamps the blocking attributes at mount time, since delegation is not yet\n * set here.\n */\nexport function ReploPixelScript({\n pixelUrl,\n apiUrl,\n projectId,\n}: {\n pixelUrl: string;\n apiUrl: string;\n projectId: string;\n}) {\n const consent = useConsent();\n if (\n !findDelegatedPlatform() &&\n !isConsentAllowed({ state: consent, requiredConsent: [\"analytics\"] })\n ) {\n return null;\n }\n\n const descriptors: ScriptTagDescriptor[] = [\n {\n kind: \"external\",\n src: `${pixelUrl}/latest.js`,\n attributes: {\n \"data-api-url\": apiUrl,\n \"data-project-id\": projectId,\n },\n },\n ];\n\n return (\n <InjectedScript\n baseId={PIXEL_SCRIPT_ID}\n descriptors={descriptors}\n requiredConsent={[\"analytics\"]}\n />\n );\n}\n"],
5
5
  "mappings": ";AAqDI;AAjDJ,SAAS,6BAA6B;AACtC,SAAS,kBAAkB,kBAAkB;AAC7C,SAAS,sBAAsB;AAI/B,MAAM,kBAAkB;AAcjB,SAAS,iBAAiB;AAAA,EAC/B;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,QAAM,UAAU,WAAW;AAC3B,MACE,CAAC,sBAAsB,KACvB,CAAC,iBAAiB,EAAE,OAAO,SAAS,iBAAiB,CAAC,WAAW,EAAE,CAAC,GACpE;AACA,WAAO;AAAA,EACT;AAEA,QAAM,cAAqC;AAAA,IACzC;AAAA,MACE,MAAM;AAAA,MACN,KAAK,GAAG,QAAQ;AAAA,MAChB,YAAY;AAAA,QACV,gBAAgB;AAAA,QAChB,mBAAmB;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAEA,SACE;AAAA,IAAC;AAAA;AAAA,MACC,QAAQ;AAAA,MACR;AAAA,MACA,iBAAiB,CAAC,WAAW;AAAA;AAAA,EAC/B;AAEJ;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,41 @@
1
+ import type { PurchasePayload } from "../analytics-sink";
2
+ type AnalyticsRecord = Record<string, unknown>;
3
+ export type ConvergeProductProperties = {
4
+ product_id: string;
5
+ name: string;
6
+ price: number;
7
+ currency: string;
8
+ variant_id?: string;
9
+ sku?: string;
10
+ variant_name?: string;
11
+ vendor?: string;
12
+ url?: string;
13
+ image_url?: string;
14
+ };
15
+ type ConvergeCartItem = ConvergeProductProperties & {
16
+ quantity: number;
17
+ };
18
+ export type ConvergeCartProperties = {
19
+ total_price: number;
20
+ total_tax: number;
21
+ total_shipping: number;
22
+ currency: string;
23
+ items?: ConvergeCartItem[];
24
+ total_discount?: number;
25
+ coupon?: string | string[];
26
+ };
27
+ export type ConvergeOrderProperties = ConvergeCartProperties & {
28
+ id: string;
29
+ };
30
+ export type ConvergeCollectionProperties = {
31
+ id: string;
32
+ name: string;
33
+ description?: string;
34
+ items?: ConvergeProductProperties[];
35
+ };
36
+ export declare function mapConvergeProduct(data: AnalyticsRecord): ConvergeProductProperties | null;
37
+ export declare function mapConvergeCartItem(data: AnalyticsRecord): ConvergeCartItem | null;
38
+ export declare function mapConvergeCollection(data: AnalyticsRecord): ConvergeCollectionProperties | null;
39
+ export declare function mapConvergeCart(data: AnalyticsRecord): ConvergeCartProperties | null;
40
+ export declare function mapConvergeOrder({ amount, currency, data, }: PurchasePayload): ConvergeOrderProperties | null;
41
+ export {};
@@ -0,0 +1,182 @@
1
+ import { z } from "zod";
2
+ const analyticsRecordSchema = z.record(z.string(), z.unknown());
3
+ const nonEmptyStringSchema = z.union([
4
+ z.string().min(1),
5
+ z.number().finite().transform(String)
6
+ ]);
7
+ const finiteNumberSchema = z.union([
8
+ z.number().finite(),
9
+ z.string().trim().min(1).transform(Number).pipe(z.number().finite())
10
+ ]);
11
+ const couponSchema = z.union([z.string(), z.array(z.string())]);
12
+ function parseRecord(value) {
13
+ const result = analyticsRecordSchema.safeParse(value);
14
+ return result.success ? result.data : null;
15
+ }
16
+ function firstValue(record, keys) {
17
+ for (const key of keys) {
18
+ const value = record[key];
19
+ if (value !== null && value !== void 0) {
20
+ return value;
21
+ }
22
+ }
23
+ return void 0;
24
+ }
25
+ function parseString(value) {
26
+ const result = nonEmptyStringSchema.safeParse(value);
27
+ return result.success ? result.data : void 0;
28
+ }
29
+ function numberValue(value) {
30
+ const result = finiteNumberSchema.safeParse(value);
31
+ return result.success ? result.data : void 0;
32
+ }
33
+ function stringFrom(record, keys) {
34
+ return parseString(firstValue(record, keys));
35
+ }
36
+ function numberFrom(record, keys) {
37
+ return numberValue(firstValue(record, keys));
38
+ }
39
+ function moneyFrom(value) {
40
+ const record = parseRecord(value);
41
+ if (!record) {
42
+ return null;
43
+ }
44
+ return {
45
+ amount: numberFrom(record, ["amount"]),
46
+ currency: stringFrom(record, ["currencyCode", "currency"])
47
+ };
48
+ }
49
+ function optionalProductProperties({
50
+ source,
51
+ variant,
52
+ product
53
+ }) {
54
+ return {
55
+ variant_id: stringFrom(source, ["variant_id", "variantId"]) ?? stringFrom(variant, ["id"]),
56
+ sku: stringFrom(source, ["sku", "variantSku"]) ?? stringFrom(variant, ["sku"]),
57
+ variant_name: stringFrom(source, ["variant_name", "variantTitle"]) ?? stringFrom(variant, ["title"]),
58
+ vendor: stringFrom(source, ["vendor", "productVendor"]) ?? stringFrom(product, ["vendor"]),
59
+ url: stringFrom(source, ["url", "productUrl"]) ?? stringFrom(product, ["url"]),
60
+ image_url: stringFrom(source, ["image_url", "imageUrl", "productImageUrl"]) ?? stringFrom(product, ["imageUrl", "image_url"])
61
+ };
62
+ }
63
+ function withoutUndefined(record) {
64
+ return Object.fromEntries(
65
+ Object.entries(record).filter(([, value]) => value !== void 0)
66
+ );
67
+ }
68
+ function mapProduct(value, defaults = {}) {
69
+ const source = parseRecord(value);
70
+ if (!source) {
71
+ return null;
72
+ }
73
+ const variant = parseRecord(source.variant) ?? {};
74
+ const product = parseRecord(variant.product) ?? parseRecord(source.product) ?? {};
75
+ const variantPrice = moneyFrom(variant.price);
76
+ const sourcePrice = moneyFrom(source.price);
77
+ const productId = stringFrom(source, ["product_id", "productId"]) ?? stringFrom(product, ["id"]);
78
+ const name = stringFrom(source, ["name", "productTitle", "title"]) ?? stringFrom(product, ["title", "name"]);
79
+ const price = numberFrom(source, ["price"]) ?? sourcePrice?.amount ?? variantPrice?.amount;
80
+ const currency = stringFrom(source, ["currency", "currencyCode"]) ?? sourcePrice?.currency ?? variantPrice?.currency ?? defaults.currency;
81
+ if (!productId || !name || price === void 0 || !currency) {
82
+ return null;
83
+ }
84
+ const quantity = numberFrom(source, ["quantity"]) ?? defaults.quantity;
85
+ const mapped = {
86
+ product_id: productId,
87
+ name,
88
+ price,
89
+ currency,
90
+ ...withoutUndefined(
91
+ optionalProductProperties({ source, variant, product })
92
+ ),
93
+ ...quantity === void 0 ? {} : { quantity }
94
+ };
95
+ return mapped;
96
+ }
97
+ function listFrom(record, keys) {
98
+ const value = firstValue(record, keys);
99
+ return Array.isArray(value) ? value : [];
100
+ }
101
+ function couponFrom(record) {
102
+ const result = couponSchema.safeParse(
103
+ firstValue(record, ["coupon", "discountCode"])
104
+ );
105
+ return result.success ? result.data : void 0;
106
+ }
107
+ function mapConvergeProduct(data) {
108
+ const product = mapProduct(data);
109
+ if (!product) {
110
+ return null;
111
+ }
112
+ const { quantity: _quantity, ...properties } = product;
113
+ return properties;
114
+ }
115
+ function mapConvergeCartItem(data) {
116
+ const product = mapProduct(data, { quantity: 1 });
117
+ if (!product || product.quantity === void 0) {
118
+ return null;
119
+ }
120
+ return { ...product, quantity: product.quantity };
121
+ }
122
+ function mapConvergeCollection(data) {
123
+ const id = stringFrom(data, ["id", "collectionId"]);
124
+ const name = stringFrom(data, ["name", "collectionTitle"]);
125
+ if (!id || !name) {
126
+ return null;
127
+ }
128
+ const items = listFrom(data, ["items", "products"]).map((item) => mapProduct(item)).filter((item) => item !== null).map(({ quantity: _quantity, ...item }) => item);
129
+ return {
130
+ id,
131
+ name,
132
+ ...withoutUndefined({
133
+ description: stringFrom(data, ["description", "collectionDescription"]),
134
+ items: items.length > 0 ? items : void 0
135
+ })
136
+ };
137
+ }
138
+ function mapConvergeCart(data) {
139
+ const totalPriceMoney = moneyFrom(firstValue(data, ["totalPrice"]));
140
+ const subtotalMoney = moneyFrom(firstValue(data, ["subtotalPrice"]));
141
+ const currency = stringFrom(data, ["currency", "currencyCode"]) ?? totalPriceMoney?.currency ?? subtotalMoney?.currency;
142
+ const totalPrice = numberFrom(data, ["total_price", "totalAmount", "subtotal"]) ?? totalPriceMoney?.amount ?? subtotalMoney?.amount;
143
+ if (totalPrice === void 0 || !currency) {
144
+ return null;
145
+ }
146
+ const items = listFrom(data, ["items", "lineItems"]).map((item) => mapProduct(item, { currency, quantity: 1 })).filter((item) => item?.quantity !== void 0);
147
+ return {
148
+ total_price: totalPrice,
149
+ total_tax: numberFrom(data, ["total_tax", "totalTax"]) ?? 0,
150
+ total_shipping: numberFrom(data, ["total_shipping", "totalShipping"]) ?? 0,
151
+ currency,
152
+ ...withoutUndefined({
153
+ total_discount: numberFrom(data, ["total_discount", "totalDiscount"]),
154
+ coupon: couponFrom(data),
155
+ items: items.length > 0 ? items : void 0
156
+ })
157
+ };
158
+ }
159
+ function mapConvergeOrder({
160
+ amount,
161
+ currency,
162
+ data
163
+ }) {
164
+ const id = stringFrom(data, ["id", "order_id", "orderId"]);
165
+ if (!id) {
166
+ return null;
167
+ }
168
+ const cart = mapConvergeCart({
169
+ ...data,
170
+ total_price: amount,
171
+ currency
172
+ });
173
+ return cart ? { id, ...cart } : null;
174
+ }
175
+ export {
176
+ mapConvergeCart,
177
+ mapConvergeCartItem,
178
+ mapConvergeCollection,
179
+ mapConvergeOrder,
180
+ mapConvergeProduct
181
+ };
182
+ //# sourceMappingURL=converge-event-mapper.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../analytics/sinks/converge-event-mapper.ts"],
4
+ "sourcesContent": ["import type { PurchasePayload } from \"../analytics-sink\";\n\nimport { z } from \"zod\";\n\ntype AnalyticsRecord = Record<string, unknown>;\n\nconst analyticsRecordSchema = z.record(z.string(), z.unknown());\nconst nonEmptyStringSchema = z.union([\n z.string().min(1),\n z.number().finite().transform(String),\n]);\nconst finiteNumberSchema = z.union([\n z.number().finite(),\n z.string().trim().min(1).transform(Number).pipe(z.number().finite()),\n]);\nconst couponSchema = z.union([z.string(), z.array(z.string())]);\n\nexport type ConvergeProductProperties = {\n product_id: string;\n name: string;\n price: number;\n currency: string;\n variant_id?: string;\n sku?: string;\n variant_name?: string;\n vendor?: string;\n url?: string;\n image_url?: string;\n};\n\ntype ConvergeCartItem = ConvergeProductProperties & { quantity: number };\ntype MappedConvergeProduct = ConvergeProductProperties & {\n quantity?: number;\n};\n\nexport type ConvergeCartProperties = {\n total_price: number;\n total_tax: number;\n total_shipping: number;\n currency: string;\n items?: ConvergeCartItem[];\n total_discount?: number;\n coupon?: string | string[];\n};\n\nexport type ConvergeOrderProperties = ConvergeCartProperties & { id: string };\n\nexport type ConvergeCollectionProperties = {\n id: string;\n name: string;\n description?: string;\n items?: ConvergeProductProperties[];\n};\n\nfunction parseRecord(value: unknown): AnalyticsRecord | null {\n const result = analyticsRecordSchema.safeParse(value);\n return result.success ? result.data : null;\n}\n\nfunction firstValue(record: AnalyticsRecord, keys: string[]): unknown {\n for (const key of keys) {\n const value = record[key];\n if (value !== null && value !== undefined) {\n return value;\n }\n }\n return undefined;\n}\n\nfunction parseString(value: unknown): string | undefined {\n const result = nonEmptyStringSchema.safeParse(value);\n return result.success ? result.data : undefined;\n}\n\nfunction numberValue(value: unknown): number | undefined {\n const result = finiteNumberSchema.safeParse(value);\n return result.success ? result.data : undefined;\n}\n\nfunction stringFrom(record: AnalyticsRecord, keys: string[]) {\n return parseString(firstValue(record, keys));\n}\n\nfunction numberFrom(record: AnalyticsRecord, keys: string[]) {\n return numberValue(firstValue(record, keys));\n}\n\nfunction moneyFrom(value: unknown) {\n const record = parseRecord(value);\n if (!record) {\n return null;\n }\n return {\n amount: numberFrom(record, [\"amount\"]),\n currency: stringFrom(record, [\"currencyCode\", \"currency\"]),\n };\n}\n\nfunction optionalProductProperties({\n source,\n variant,\n product,\n}: {\n source: AnalyticsRecord;\n variant: AnalyticsRecord;\n product: AnalyticsRecord;\n}) {\n return {\n variant_id:\n stringFrom(source, [\"variant_id\", \"variantId\"]) ??\n stringFrom(variant, [\"id\"]),\n sku:\n stringFrom(source, [\"sku\", \"variantSku\"]) ?? stringFrom(variant, [\"sku\"]),\n variant_name:\n stringFrom(source, [\"variant_name\", \"variantTitle\"]) ??\n stringFrom(variant, [\"title\"]),\n vendor:\n stringFrom(source, [\"vendor\", \"productVendor\"]) ??\n stringFrom(product, [\"vendor\"]),\n url:\n stringFrom(source, [\"url\", \"productUrl\"]) ?? stringFrom(product, [\"url\"]),\n image_url:\n stringFrom(source, [\"image_url\", \"imageUrl\", \"productImageUrl\"]) ??\n stringFrom(product, [\"imageUrl\", \"image_url\"]),\n };\n}\n\nfunction withoutUndefined<RecordType extends AnalyticsRecord>(\n record: RecordType,\n): Partial<RecordType> {\n // oxlint-disable-next-line replo/no-as-cast -- Object.fromEntries cannot preserve the input key mapping after filtering\n return Object.fromEntries(\n Object.entries(record).filter(([, value]) => value !== undefined),\n ) as Partial<RecordType>;\n}\n\nfunction mapProduct(\n value: unknown,\n defaults: { currency?: string; quantity?: number } = {},\n): MappedConvergeProduct | null {\n const source = parseRecord(value);\n if (!source) {\n return null;\n }\n\n const variant = parseRecord(source.variant) ?? {};\n const product =\n parseRecord(variant.product) ?? parseRecord(source.product) ?? {};\n const variantPrice = moneyFrom(variant.price);\n const sourcePrice = moneyFrom(source.price);\n\n const productId =\n stringFrom(source, [\"product_id\", \"productId\"]) ??\n stringFrom(product, [\"id\"]);\n const name =\n stringFrom(source, [\"name\", \"productTitle\", \"title\"]) ??\n stringFrom(product, [\"title\", \"name\"]);\n const price =\n numberFrom(source, [\"price\"]) ??\n sourcePrice?.amount ??\n variantPrice?.amount;\n const currency =\n stringFrom(source, [\"currency\", \"currencyCode\"]) ??\n sourcePrice?.currency ??\n variantPrice?.currency ??\n defaults.currency;\n\n if (!productId || !name || price === undefined || !currency) {\n return null;\n }\n\n const quantity = numberFrom(source, [\"quantity\"]) ?? defaults.quantity;\n const mapped: MappedConvergeProduct = {\n product_id: productId,\n name,\n price,\n currency,\n ...withoutUndefined(\n optionalProductProperties({ source, variant, product }),\n ),\n ...(quantity === undefined ? {} : { quantity }),\n };\n return mapped;\n}\n\nfunction listFrom(record: AnalyticsRecord, keys: string[]): unknown[] {\n const value = firstValue(record, keys);\n return Array.isArray(value) ? value : [];\n}\n\nfunction couponFrom(record: AnalyticsRecord): string | string[] | undefined {\n const result = couponSchema.safeParse(\n firstValue(record, [\"coupon\", \"discountCode\"]),\n );\n return result.success ? result.data : undefined;\n}\n\nexport function mapConvergeProduct(\n data: AnalyticsRecord,\n): ConvergeProductProperties | null {\n const product = mapProduct(data);\n if (!product) {\n return null;\n }\n const { quantity: _quantity, ...properties } = product;\n return properties;\n}\n\nexport function mapConvergeCartItem(\n data: AnalyticsRecord,\n): ConvergeCartItem | null {\n const product = mapProduct(data, { quantity: 1 });\n if (!product || product.quantity === undefined) {\n return null;\n }\n return { ...product, quantity: product.quantity };\n}\n\nexport function mapConvergeCollection(\n data: AnalyticsRecord,\n): ConvergeCollectionProperties | null {\n const id = stringFrom(data, [\"id\", \"collectionId\"]);\n const name = stringFrom(data, [\"name\", \"collectionTitle\"]);\n if (!id || !name) {\n return null;\n }\n const items = listFrom(data, [\"items\", \"products\"])\n .map((item) => mapProduct(item))\n .filter((item): item is MappedConvergeProduct => item !== null)\n .map(({ quantity: _quantity, ...item }) => item);\n\n return {\n id,\n name,\n ...withoutUndefined({\n description: stringFrom(data, [\"description\", \"collectionDescription\"]),\n items: items.length > 0 ? items : undefined,\n }),\n };\n}\n\nexport function mapConvergeCart(\n data: AnalyticsRecord,\n): ConvergeCartProperties | null {\n const totalPriceMoney = moneyFrom(firstValue(data, [\"totalPrice\"]));\n const subtotalMoney = moneyFrom(firstValue(data, [\"subtotalPrice\"]));\n const currency =\n stringFrom(data, [\"currency\", \"currencyCode\"]) ??\n totalPriceMoney?.currency ??\n subtotalMoney?.currency;\n const totalPrice =\n numberFrom(data, [\"total_price\", \"totalAmount\", \"subtotal\"]) ??\n totalPriceMoney?.amount ??\n subtotalMoney?.amount;\n if (totalPrice === undefined || !currency) {\n return null;\n }\n\n const items = listFrom(data, [\"items\", \"lineItems\"])\n .map((item) => mapProduct(item, { currency, quantity: 1 }))\n .filter((item): item is ConvergeCartItem => item?.quantity !== undefined);\n\n return {\n total_price: totalPrice,\n total_tax: numberFrom(data, [\"total_tax\", \"totalTax\"]) ?? 0,\n total_shipping: numberFrom(data, [\"total_shipping\", \"totalShipping\"]) ?? 0,\n currency,\n ...withoutUndefined({\n total_discount: numberFrom(data, [\"total_discount\", \"totalDiscount\"]),\n coupon: couponFrom(data),\n items: items.length > 0 ? items : undefined,\n }),\n };\n}\n\nexport function mapConvergeOrder({\n amount,\n currency,\n data,\n}: PurchasePayload): ConvergeOrderProperties | null {\n const id = stringFrom(data, [\"id\", \"order_id\", \"orderId\"]);\n if (!id) {\n return null;\n }\n const cart = mapConvergeCart({\n ...data,\n total_price: amount,\n currency,\n });\n return cart ? { id, ...cart } : null;\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -1,16 +1,30 @@
1
1
  import type { AnalyticsSink, DataPayload, PurchasePayload } from "../analytics-sink";
2
+ import type { PageMetadata } from "../utils/navigation-tracker";
3
+ type ConvergeCall = {
4
+ method: "track";
5
+ eventName: string;
6
+ properties?: Record<string, unknown>;
7
+ } | {
8
+ method: "forward";
9
+ eventName: "Placed Order";
10
+ eventID: string;
11
+ properties: Record<string, unknown>;
12
+ };
13
+ declare global {
14
+ interface Window {
15
+ cvg?: (call: ConvergeCall) => void;
16
+ }
17
+ }
2
18
  /**
3
- * Event names and property shapes follow Converge's client-side event spec
4
- * (https://docs.runconverge.com/sources/converge-spec); our funnel payload keys
5
- * already match the spec's property names, so payloads pass through unmapped.
6
- * The Converge snippet does not self-fire `$page_load`, so this sink owns every
7
- * pageview: the NavigationTracker broadcast covers both the initial load and
8
- * soft navigations, which is Converge's documented SPA integration.
19
+ * The consent-gated Converge snippet owns the initial pageview. This sink maps
20
+ * Replo's analytics payloads to Converge's strict event spec and adds SPA
21
+ * pageviews after client-side navigations.
9
22
  */
10
23
  export declare class ConvergeSink implements AnalyticsSink {
11
- private track;
12
- pageView(): Promise<void>;
13
- purchase({ amount, currency, data }: PurchasePayload): Promise<void>;
24
+ private send;
25
+ private trackIfValid;
26
+ pageView(metadata?: PageMetadata): Promise<void>;
27
+ purchase(payload: PurchasePayload): Promise<void>;
14
28
  click(): Promise<void>;
15
29
  productViewed({ data }: DataPayload): Promise<void>;
16
30
  addToCart({ data }: DataPayload): Promise<void>;
@@ -19,3 +33,4 @@ export declare class ConvergeSink implements AnalyticsSink {
19
33
  startCheckout({ data }: DataPayload): Promise<void>;
20
34
  viewCollection({ data }: DataPayload): Promise<void>;
21
35
  }
36
+ export {};
@@ -1,48 +1,70 @@
1
- import { createEventQueue } from "../utils/analytics-utils";
2
- function cvgQueue() {
1
+ import { createAnalyticsQueue } from "../utils/analytics-utils";
2
+ import {
3
+ mapConvergeCart,
4
+ mapConvergeCartItem,
5
+ mapConvergeCollection,
6
+ mapConvergeOrder,
7
+ mapConvergeProduct
8
+ } from "./converge-event-mapper";
9
+ function findConvergeQueue() {
3
10
  if (typeof window === "undefined") {
4
11
  return null;
5
12
  }
6
13
  return window.cvg ?? null;
7
14
  }
8
15
  class ConvergeSink {
9
- track = createEventQueue(
10
- () => cvgQueue() !== null,
11
- (eventName, properties) => {
12
- cvgQueue()?.({ method: "track", eventName, properties });
16
+ send = createAnalyticsQueue({
17
+ isReady: () => Boolean(findConvergeQueue()),
18
+ send: (call) => {
19
+ findConvergeQueue()?.(call);
13
20
  },
14
- "Converge"
15
- );
16
- async pageView() {
17
- this.track("$page_load");
18
- }
19
- async purchase({ amount, currency, data }) {
20
- this.track("Placed Order", {
21
- ...data,
22
- total_price: amount,
23
- currency,
24
- ...data.order_id === null || data.order_id === void 0 ? {} : { eventID: String(data.order_id) }
21
+ queueName: "Converge"
22
+ });
23
+ trackIfValid(eventName, properties) {
24
+ if (!properties) {
25
+ console.warn(`[analytics] skipped invalid Converge ${eventName} event`);
26
+ return;
27
+ }
28
+ this.send({ method: "track", eventName, properties });
29
+ }
30
+ async pageView(metadata) {
31
+ if (metadata?.navigationType === "initial") {
32
+ return;
33
+ }
34
+ this.send({ method: "track", eventName: "$page_load" });
35
+ }
36
+ async purchase(payload) {
37
+ const properties = mapConvergeOrder(payload);
38
+ if (!properties) {
39
+ console.warn("[analytics] skipped invalid Converge Placed Order event");
40
+ return;
41
+ }
42
+ this.send({
43
+ method: "forward",
44
+ eventName: "Placed Order",
45
+ eventID: properties.id,
46
+ properties
25
47
  });
26
48
  }
27
49
  async click() {
28
50
  }
29
51
  async productViewed({ data }) {
30
- this.track("Viewed Product", data);
52
+ this.trackIfValid("Viewed Product", mapConvergeProduct(data));
31
53
  }
32
54
  async addToCart({ data }) {
33
- this.track("Added To Cart", { ...data, quantity: data.quantity ?? 1 });
55
+ this.trackIfValid("Added To Cart", mapConvergeCartItem(data));
34
56
  }
35
57
  async removeFromCart({ data }) {
36
- this.track("Removed From Cart", { ...data, quantity: data.quantity ?? 1 });
58
+ this.trackIfValid("Removed From Cart", mapConvergeCartItem(data));
37
59
  }
38
60
  async viewCart({ data }) {
39
- this.track("Viewed Cart", data);
61
+ this.trackIfValid("Viewed Cart", mapConvergeCart(data));
40
62
  }
41
63
  async startCheckout({ data }) {
42
- this.track("Started Checkout", data);
64
+ this.trackIfValid("Started Checkout", mapConvergeCart(data));
43
65
  }
44
66
  async viewCollection({ data }) {
45
- this.track("Viewed Collection", data);
67
+ this.trackIfValid("Viewed Collection", mapConvergeCollection(data));
46
68
  }
47
69
  }
48
70
  export {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../analytics/sinks/converge-sink.ts"],
4
- "sourcesContent": ["import type {\n AnalyticsSink,\n DataPayload,\n PurchasePayload,\n} from \"../analytics-sink\";\n\nimport { createEventQueue } from \"../utils/analytics-utils\";\n\ntype ConvergeTrackCall = {\n method: \"track\";\n eventName: string;\n properties?: Record<string, unknown>;\n};\n\ntype WindowWithCvg = Window & { cvg?: (call: ConvergeTrackCall) => void };\n\nfunction cvgQueue(): ((call: ConvergeTrackCall) => void) | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n // oxlint-disable-next-line replo/no-as-cast -- the pixel's queue stub is an untyped window global; this is the one place it's narrowed to its known call shape\n return (window as WindowWithCvg).cvg ?? null;\n}\n\n/**\n * Event names and property shapes follow Converge's client-side event spec\n * (https://docs.runconverge.com/sources/converge-spec); our funnel payload keys\n * already match the spec's property names, so payloads pass through unmapped.\n * The Converge snippet does not self-fire `$page_load`, so this sink owns every\n * pageview: the NavigationTracker broadcast covers both the initial load and\n * soft navigations, which is Converge's documented SPA integration.\n */\nexport class ConvergeSink implements AnalyticsSink {\n private track = createEventQueue(\n () => cvgQueue() !== null,\n (eventName, properties) => {\n cvgQueue()?.({ method: \"track\", eventName, properties });\n },\n \"Converge\",\n );\n\n async pageView() {\n this.track(\"$page_load\");\n }\n\n async purchase({ amount, currency, data }: PurchasePayload) {\n // `eventID` dedupes the client-side conversion against Converge's\n // server-side order source (e.g. their Shopify backend integration).\n this.track(\"Placed Order\", {\n ...data,\n total_price: amount,\n currency,\n ...(data.order_id === null || data.order_id === undefined\n ? {}\n : { eventID: String(data.order_id) }),\n });\n }\n\n async click() {\n // No Converge equivalent\n }\n\n async productViewed({ data }: DataPayload) {\n this.track(\"Viewed Product\", data);\n }\n\n async addToCart({ data }: DataPayload) {\n this.track(\"Added To Cart\", { ...data, quantity: data.quantity ?? 1 });\n }\n\n async removeFromCart({ data }: DataPayload) {\n this.track(\"Removed From Cart\", { ...data, quantity: data.quantity ?? 1 });\n }\n\n async viewCart({ data }: DataPayload) {\n this.track(\"Viewed Cart\", data);\n }\n\n async startCheckout({ data }: DataPayload) {\n this.track(\"Started Checkout\", data);\n }\n\n async viewCollection({ data }: DataPayload) {\n this.track(\"Viewed Collection\", data);\n }\n}\n"],
5
- "mappings": "AAMA,SAAS,wBAAwB;AAUjC,SAAS,WAAuD;AAC9D,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,SAAQ,OAAyB,OAAO;AAC1C;AAUO,MAAM,aAAsC;AAAA,EACzC,QAAQ;AAAA,IACd,MAAM,SAAS,MAAM;AAAA,IACrB,CAAC,WAAW,eAAe;AACzB,eAAS,IAAI,EAAE,QAAQ,SAAS,WAAW,WAAW,CAAC;AAAA,IACzD;AAAA,IACA;AAAA,EACF;AAAA,EAEA,MAAM,WAAW;AACf,SAAK,MAAM,YAAY;AAAA,EACzB;AAAA,EAEA,MAAM,SAAS,EAAE,QAAQ,UAAU,KAAK,GAAoB;AAG1D,SAAK,MAAM,gBAAgB;AAAA,MACzB,GAAG;AAAA,MACH,aAAa;AAAA,MACb;AAAA,MACA,GAAI,KAAK,aAAa,QAAQ,KAAK,aAAa,SAC5C,CAAC,IACD,EAAE,SAAS,OAAO,KAAK,QAAQ,EAAE;AAAA,IACvC,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ;AAAA,EAEd;AAAA,EAEA,MAAM,cAAc,EAAE,KAAK,GAAgB;AACzC,SAAK,MAAM,kBAAkB,IAAI;AAAA,EACnC;AAAA,EAEA,MAAM,UAAU,EAAE,KAAK,GAAgB;AACrC,SAAK,MAAM,iBAAiB,EAAE,GAAG,MAAM,UAAU,KAAK,YAAY,EAAE,CAAC;AAAA,EACvE;AAAA,EAEA,MAAM,eAAe,EAAE,KAAK,GAAgB;AAC1C,SAAK,MAAM,qBAAqB,EAAE,GAAG,MAAM,UAAU,KAAK,YAAY,EAAE,CAAC;AAAA,EAC3E;AAAA,EAEA,MAAM,SAAS,EAAE,KAAK,GAAgB;AACpC,SAAK,MAAM,eAAe,IAAI;AAAA,EAChC;AAAA,EAEA,MAAM,cAAc,EAAE,KAAK,GAAgB;AACzC,SAAK,MAAM,oBAAoB,IAAI;AAAA,EACrC;AAAA,EAEA,MAAM,eAAe,EAAE,KAAK,GAAgB;AAC1C,SAAK,MAAM,qBAAqB,IAAI;AAAA,EACtC;AACF;",
4
+ "sourcesContent": ["import type {\n AnalyticsSink,\n DataPayload,\n PurchasePayload,\n} from \"../analytics-sink\";\nimport type { PageMetadata } from \"../utils/navigation-tracker\";\n\nimport { createAnalyticsQueue } from \"../utils/analytics-utils\";\nimport {\n mapConvergeCart,\n mapConvergeCartItem,\n mapConvergeCollection,\n mapConvergeOrder,\n mapConvergeProduct,\n} from \"./converge-event-mapper\";\n\ntype ConvergeCall =\n | {\n method: \"track\";\n eventName: string;\n properties?: Record<string, unknown>;\n }\n | {\n method: \"forward\";\n eventName: \"Placed Order\";\n eventID: string;\n properties: Record<string, unknown>;\n };\n\ndeclare global {\n interface Window {\n cvg?: (call: ConvergeCall) => void;\n }\n}\n\nfunction findConvergeQueue(): ((call: ConvergeCall) => void) | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n return window.cvg ?? null;\n}\n\n/**\n * The consent-gated Converge snippet owns the initial pageview. This sink maps\n * Replo's analytics payloads to Converge's strict event spec and adds SPA\n * pageviews after client-side navigations.\n */\nexport class ConvergeSink implements AnalyticsSink {\n private send = createAnalyticsQueue<ConvergeCall>({\n isReady: () => Boolean(findConvergeQueue()),\n send: (call) => {\n findConvergeQueue()?.(call);\n },\n queueName: \"Converge\",\n });\n\n private trackIfValid(\n eventName: string,\n properties: Record<string, unknown> | null,\n ) {\n if (!properties) {\n console.warn(`[analytics] skipped invalid Converge ${eventName} event`);\n return;\n }\n this.send({ method: \"track\", eventName, properties });\n }\n\n async pageView(metadata?: PageMetadata) {\n if (metadata?.navigationType === \"initial\") {\n return;\n }\n this.send({ method: \"track\", eventName: \"$page_load\" });\n }\n\n async purchase(payload: PurchasePayload) {\n const properties = mapConvergeOrder(payload);\n if (!properties) {\n console.warn(\"[analytics] skipped invalid Converge Placed Order event\");\n return;\n }\n this.send({\n method: \"forward\",\n eventName: \"Placed Order\",\n eventID: properties.id,\n properties,\n });\n }\n\n async click() {\n // No Converge equivalent\n }\n\n async productViewed({ data }: DataPayload) {\n this.trackIfValid(\"Viewed Product\", mapConvergeProduct(data));\n }\n\n async addToCart({ data }: DataPayload) {\n this.trackIfValid(\"Added To Cart\", mapConvergeCartItem(data));\n }\n\n async removeFromCart({ data }: DataPayload) {\n this.trackIfValid(\"Removed From Cart\", mapConvergeCartItem(data));\n }\n\n async viewCart({ data }: DataPayload) {\n this.trackIfValid(\"Viewed Cart\", mapConvergeCart(data));\n }\n\n async startCheckout({ data }: DataPayload) {\n this.trackIfValid(\"Started Checkout\", mapConvergeCart(data));\n }\n\n async viewCollection({ data }: DataPayload) {\n this.trackIfValid(\"Viewed Collection\", mapConvergeCollection(data));\n }\n}\n"],
5
+ "mappings": "AAOA,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAqBP,SAAS,oBAA2D;AAClE,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AACA,SAAO,OAAO,OAAO;AACvB;AAOO,MAAM,aAAsC;AAAA,EACzC,OAAO,qBAAmC;AAAA,IAChD,SAAS,MAAM,QAAQ,kBAAkB,CAAC;AAAA,IAC1C,MAAM,CAAC,SAAS;AACd,wBAAkB,IAAI,IAAI;AAAA,IAC5B;AAAA,IACA,WAAW;AAAA,EACb,CAAC;AAAA,EAEO,aACN,WACA,YACA;AACA,QAAI,CAAC,YAAY;AACf,cAAQ,KAAK,wCAAwC,SAAS,QAAQ;AACtE;AAAA,IACF;AACA,SAAK,KAAK,EAAE,QAAQ,SAAS,WAAW,WAAW,CAAC;AAAA,EACtD;AAAA,EAEA,MAAM,SAAS,UAAyB;AACtC,QAAI,UAAU,mBAAmB,WAAW;AAC1C;AAAA,IACF;AACA,SAAK,KAAK,EAAE,QAAQ,SAAS,WAAW,aAAa,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,SAAS,SAA0B;AACvC,UAAM,aAAa,iBAAiB,OAAO;AAC3C,QAAI,CAAC,YAAY;AACf,cAAQ,KAAK,yDAAyD;AACtE;AAAA,IACF;AACA,SAAK,KAAK;AAAA,MACR,QAAQ;AAAA,MACR,WAAW;AAAA,MACX,SAAS,WAAW;AAAA,MACpB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,QAAQ;AAAA,EAEd;AAAA,EAEA,MAAM,cAAc,EAAE,KAAK,GAAgB;AACzC,SAAK,aAAa,kBAAkB,mBAAmB,IAAI,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,UAAU,EAAE,KAAK,GAAgB;AACrC,SAAK,aAAa,iBAAiB,oBAAoB,IAAI,CAAC;AAAA,EAC9D;AAAA,EAEA,MAAM,eAAe,EAAE,KAAK,GAAgB;AAC1C,SAAK,aAAa,qBAAqB,oBAAoB,IAAI,CAAC;AAAA,EAClE;AAAA,EAEA,MAAM,SAAS,EAAE,KAAK,GAAgB;AACpC,SAAK,aAAa,eAAe,gBAAgB,IAAI,CAAC;AAAA,EACxD;AAAA,EAEA,MAAM,cAAc,EAAE,KAAK,GAAgB;AACzC,SAAK,aAAa,oBAAoB,gBAAgB,IAAI,CAAC;AAAA,EAC7D;AAAA,EAEA,MAAM,eAAe,EAAE,KAAK,GAAgB;AAC1C,SAAK,aAAa,qBAAqB,sBAAsB,IAAI,CAAC;AAAA,EACpE;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,10 +1,8 @@
1
1
  import type { AnalyticsSink } from "../analytics-sink";
2
2
  import type { PageMetadata } from "../utils/navigation-tracker";
3
3
  /**
4
- * The Northbeam snippet does not self-fire `trackPageViewInitial`, so this
5
- * sink owns every pageview: the NavigationTracker broadcast covers both the
6
- * initial load and soft navigations. Purchases are not fired client-side —
7
- * Northbeam ingests orders server-side via its own commerce integrations.
4
+ * The consent-gated Northbeam snippet owns the initial pageview. This sink only
5
+ * adds the virtual pageviews that Northbeam requires for SPA navigations.
8
6
  */
9
7
  export declare class NorthbeamSink implements AnalyticsSink {
10
8
  private call;
@@ -5,15 +5,12 @@ function northbeamPixel() {
5
5
  }
6
6
  return window.Northbeam ?? null;
7
7
  }
8
- let hasFiredInitialPageView = false;
9
8
  class NorthbeamSink {
10
9
  call = createMethodQueue(northbeamPixel, {
11
10
  queueName: "Northbeam"
12
11
  });
13
12
  async pageView(metadata) {
14
- if (!hasFiredInitialPageView) {
15
- hasFiredInitialPageView = true;
16
- this.call("trackPageViewInitial", metadata?.url ?? window.location.href);
13
+ if (metadata?.navigationType === "initial") {
17
14
  return;
18
15
  }
19
16
  this.call("trackPageView");
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../analytics/sinks/northbeam-sink.ts"],
4
- "sourcesContent": ["import type { AnalyticsSink } from \"../analytics-sink\";\nimport type { PageMetadata } from \"../utils/navigation-tracker\";\n\nimport { createMethodQueue } from \"../utils/analytics-utils\";\n\ntype NorthbeamPixel = Record<string, (...args: unknown[]) => void>;\n\ntype WindowWithNorthbeam = Window & { Northbeam?: NorthbeamPixel };\n\nfunction northbeamPixel(): NorthbeamPixel | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n // oxlint-disable-next-line replo/no-as-cast -- the pixel's method map is an untyped window global; this is the one place it's narrowed to its known shape\n return (window as WindowWithNorthbeam).Northbeam ?? null;\n}\n\n// Northbeam distinguishes the first pageview of a page load\n// (`trackPageViewInitial`) from subsequent SPA pageviews (`trackPageView`).\n// The flag lives at module scope so it survives sink rebuilds (sinks are\n// recreated whenever consent or script registration changes) and still resets\n// on every hard page load.\nlet hasFiredInitialPageView = false;\n\n/**\n * The Northbeam snippet does not self-fire `trackPageViewInitial`, so this\n * sink owns every pageview: the NavigationTracker broadcast covers both the\n * initial load and soft navigations. Purchases are not fired client-side \u2014\n * Northbeam ingests orders server-side via its own commerce integrations.\n */\nexport class NorthbeamSink implements AnalyticsSink {\n private call = createMethodQueue<NorthbeamPixel>(northbeamPixel, {\n queueName: \"Northbeam\",\n });\n\n async pageView(metadata?: PageMetadata) {\n if (!hasFiredInitialPageView) {\n hasFiredInitialPageView = true;\n this.call(\"trackPageViewInitial\", metadata?.url ?? window.location.href);\n return;\n }\n this.call(\"trackPageView\");\n }\n\n async purchase() {\n // Northbeam ingests orders server-side via its commerce integrations\n }\n\n async click() {\n // No Northbeam equivalent\n }\n\n async productViewed() {\n // No Northbeam equivalent\n }\n\n async addToCart() {\n // No Northbeam equivalent\n }\n\n async removeFromCart() {\n // No Northbeam equivalent\n }\n\n async viewCart() {\n // No Northbeam equivalent\n }\n\n async startCheckout() {\n // No Northbeam equivalent\n }\n\n async viewCollection() {\n // No Northbeam equivalent\n }\n}\n"],
5
- "mappings": "AAGA,SAAS,yBAAyB;AAMlC,SAAS,iBAAwC;AAC/C,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,SAAQ,OAA+B,aAAa;AACtD;AAOA,IAAI,0BAA0B;AAQvB,MAAM,cAAuC;AAAA,EAC1C,OAAO,kBAAkC,gBAAgB;AAAA,IAC/D,WAAW;AAAA,EACb,CAAC;AAAA,EAED,MAAM,SAAS,UAAyB;AACtC,QAAI,CAAC,yBAAyB;AAC5B,gCAA0B;AAC1B,WAAK,KAAK,wBAAwB,UAAU,OAAO,OAAO,SAAS,IAAI;AACvE;AAAA,IACF;AACA,SAAK,KAAK,eAAe;AAAA,EAC3B;AAAA,EAEA,MAAM,WAAW;AAAA,EAEjB;AAAA,EAEA,MAAM,QAAQ;AAAA,EAEd;AAAA,EAEA,MAAM,gBAAgB;AAAA,EAEtB;AAAA,EAEA,MAAM,YAAY;AAAA,EAElB;AAAA,EAEA,MAAM,iBAAiB;AAAA,EAEvB;AAAA,EAEA,MAAM,WAAW;AAAA,EAEjB;AAAA,EAEA,MAAM,gBAAgB;AAAA,EAEtB;AAAA,EAEA,MAAM,iBAAiB;AAAA,EAEvB;AACF;",
4
+ "sourcesContent": ["import type { AnalyticsSink } from \"../analytics-sink\";\nimport type { PageMetadata } from \"../utils/navigation-tracker\";\n\nimport { createMethodQueue } from \"../utils/analytics-utils\";\n\ntype NorthbeamPixel = Record<string, (...args: unknown[]) => void>;\n\ntype WindowWithNorthbeam = Window & { Northbeam?: NorthbeamPixel };\n\nfunction northbeamPixel(): NorthbeamPixel | null {\n if (typeof window === \"undefined\") {\n return null;\n }\n // oxlint-disable-next-line replo/no-as-cast -- the pixel's method map is an untyped window global; this is the one place it's narrowed to its known shape\n return (window as WindowWithNorthbeam).Northbeam ?? null;\n}\n\n/**\n * The consent-gated Northbeam snippet owns the initial pageview. This sink only\n * adds the virtual pageviews that Northbeam requires for SPA navigations.\n */\nexport class NorthbeamSink implements AnalyticsSink {\n private call = createMethodQueue<NorthbeamPixel>(northbeamPixel, {\n queueName: \"Northbeam\",\n });\n\n async pageView(metadata?: PageMetadata) {\n if (metadata?.navigationType === \"initial\") {\n return;\n }\n this.call(\"trackPageView\");\n }\n\n async purchase() {\n // Northbeam ingests orders server-side via its commerce integrations\n }\n\n async click() {\n // No Northbeam equivalent\n }\n\n async productViewed() {\n // No Northbeam equivalent\n }\n\n async addToCart() {\n // No Northbeam equivalent\n }\n\n async removeFromCart() {\n // No Northbeam equivalent\n }\n\n async viewCart() {\n // No Northbeam equivalent\n }\n\n async startCheckout() {\n // No Northbeam equivalent\n }\n\n async viewCollection() {\n // No Northbeam equivalent\n }\n}\n"],
5
+ "mappings": "AAGA,SAAS,yBAAyB;AAMlC,SAAS,iBAAwC;AAC/C,MAAI,OAAO,WAAW,aAAa;AACjC,WAAO;AAAA,EACT;AAEA,SAAQ,OAA+B,aAAa;AACtD;AAMO,MAAM,cAAuC;AAAA,EAC1C,OAAO,kBAAkC,gBAAgB;AAAA,IAC/D,WAAW;AAAA,EACb,CAAC;AAAA,EAED,MAAM,SAAS,UAAyB;AACtC,QAAI,UAAU,mBAAmB,WAAW;AAC1C;AAAA,IACF;AACA,SAAK,KAAK,eAAe;AAAA,EAC3B;AAAA,EAEA,MAAM,WAAW;AAAA,EAEjB;AAAA,EAEA,MAAM,QAAQ;AAAA,EAEd;AAAA,EAEA,MAAM,gBAAgB;AAAA,EAEtB;AAAA,EAEA,MAAM,YAAY;AAAA,EAElB;AAAA,EAEA,MAAM,iBAAiB;AAAA,EAEvB;AAAA,EAEA,MAAM,WAAW;AAAA,EAEjB;AAAA,EAEA,MAAM,gBAAgB;AAAA,EAEtB;AAAA,EAEA,MAAM,iBAAiB;AAAA,EAEvB;AACF;",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,9 @@
1
- export declare function createEventQueue(checkPixelReady: () => boolean, sendEvent: (event: string, params: Record<string, any>) => void, pixelName: string): (event: string, params?: Record<string, any>) => void;
1
+ export declare function createAnalyticsQueue<Payload>({ isReady, send, queueName, }: {
2
+ isReady: () => boolean;
3
+ send: (payload: Payload) => void;
4
+ queueName: string;
5
+ }): (payload: Payload) => void;
6
+ export declare function createEventQueue(checkPixelReady: () => boolean, sendEvent: (event: string, params: Record<string, unknown>) => void, pixelName: string): (event: string, params?: Record<string, unknown>) => void;
2
7
  type MethodQueueOptions = {
3
8
  queueName?: string;
4
9
  onTimeout?: () => void;