@replohq/sdk 2.3.0 → 2.5.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.
@@ -22,3 +22,4 @@ export declare function getHmacSha256Hex({ rawBody, signingKey, }: {
22
22
  rawBody: string;
23
23
  signingKey: string;
24
24
  }): Promise<string>;
25
+ export declare function deterministicUUID(value: string): string;
@@ -19,11 +19,22 @@ export type XPixelEvents = {
19
19
  startCheckout?: string;
20
20
  purchase?: string;
21
21
  };
22
+ export type ScriptRuntimeOwner = {
23
+ type: "gtm";
24
+ containerId: string;
25
+ } | {
26
+ type: "external";
27
+ };
22
28
  export type MixpanelRegion = "us" | "eu" | "in";
23
29
  export type ReploScriptEntry = {
24
- type: "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "HubSpot";
30
+ type: "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "HubSpot";
31
+ identifier: string;
32
+ requiredConsent?: ConsentCategory[];
33
+ } | {
34
+ type: "GA4";
25
35
  identifier: string;
26
36
  requiredConsent?: ConsentCategory[];
37
+ runtimeOwner?: ScriptRuntimeOwner;
27
38
  } | {
28
39
  type: "X";
29
40
  identifier: string;
@@ -1,4 +1,4 @@
1
- import { ReploError } from '../../chunk-CKRRL66R.mjs';
1
+ import { ReploError } from '../../chunk-AE5H7AJS.mjs';
2
2
  import { __commonJS, __toESM } from '../../chunk-UJCSKKID.mjs';
3
3
 
4
4
  var require_first_match = __commonJS({
@@ -1,5 +1,5 @@
1
1
  import '../../../chunk-XSMYSAKY.mjs';
2
- import { ReploError } from '../../../chunk-CKRRL66R.mjs';
2
+ import { ReploError } from '../../../chunk-AE5H7AJS.mjs';
3
3
  import '../../../chunk-XGXJOHUY.mjs';
4
4
  import '../../../chunk-XWWFRIXV.mjs';
5
5
  import '../../../chunk-OPKMOW43.mjs';
@@ -1,6 +1,6 @@
1
1
  import { getParamSegment, getRouteParams, hasRouteParam, matchSource } from '../../../chunk-VPEVERWE.mjs';
2
2
  import '../../../chunk-XSMYSAKY.mjs';
3
- import { ReploError } from '../../../chunk-CKRRL66R.mjs';
3
+ import { ReploError } from '../../../chunk-AE5H7AJS.mjs';
4
4
  import '../../../chunk-XGXJOHUY.mjs';
5
5
  import '../../../chunk-XWWFRIXV.mjs';
6
6
  import '../../../chunk-OPKMOW43.mjs';
@@ -10,6 +10,11 @@ import {
10
10
  loadCartFromCookie,
11
11
  saveCartToCookie
12
12
  } from "./utils/cookie-cart-persistence";
13
+ import {
14
+ SHARED_SHOPIFY_CART_COOKIE_NAME,
15
+ SHOPIFY_CART_ID_PREFIX,
16
+ shareShopifyCart
17
+ } from "./utils/shopify-shared-cart";
13
18
  const SHOPIFY_CART_COOKIE_NAME = "replo_cart_id";
14
19
  async function reconcileExistingCart(cart, country) {
15
20
  const cartGateway = await getCartGateway();
@@ -42,11 +47,20 @@ async function getCartAction() {
42
47
  return await loadCartFromCookie(REPLO_CART_COOKIE_NAME);
43
48
  }
44
49
  const cookieStore = await cookies();
50
+ const cartGateway = await getCartGateway();
51
+ const sharedToken = checkoutProvider === "shopify" ? cookieStore.get(SHARED_SHOPIFY_CART_COOKIE_NAME)?.value : void 0;
52
+ if (sharedToken) {
53
+ const sharedCart = await cartGateway.getCart(
54
+ `${SHOPIFY_CART_ID_PREFIX}${sharedToken}`
55
+ );
56
+ if (sharedCart) {
57
+ return sharedCart;
58
+ }
59
+ }
45
60
  const existingCartId = cookieStore.get(SHOPIFY_CART_COOKIE_NAME)?.value;
46
61
  if (!existingCartId) {
47
62
  return null;
48
63
  }
49
- const cartGateway = await getCartGateway();
50
64
  const cart = await cartGateway.getCart(existingCartId);
51
65
  if (!cart) {
52
66
  try {
@@ -58,6 +72,9 @@ async function getCartAction() {
58
72
  console.warn("Failed to delete cart cookie:", error);
59
73
  }
60
74
  }
75
+ if (cart && checkoutProvider === "shopify") {
76
+ await shareShopifyCart(cart);
77
+ }
61
78
  return cart;
62
79
  }
63
80
  async function createCartAction(options) {
@@ -87,6 +104,9 @@ async function createCartAction(options) {
87
104
  cart.id,
88
105
  getCartCookieOptions()
89
106
  );
107
+ if (checkoutProvider === "shopify") {
108
+ await shareShopifyCart(cart);
109
+ }
90
110
  }
91
111
  } catch (cookieError) {
92
112
  console.warn("Failed to set cart cookie:", cookieError);
@@ -0,0 +1,9 @@
1
+ import type { Cart } from "../cart-types";
2
+ export declare const SHARED_SHOPIFY_CART_COOKIE_NAME = "replo_shared_cart";
3
+ export declare const SHOPIFY_CART_ID_PREFIX = "gid://shopify/Cart/";
4
+ /**
5
+ * Shopify reports the store's primary domain as the checkout URL host, so a
6
+ * site served from a subdomain of it (shop.brand.com next to brand.com) can
7
+ * share its cart with the theme without any per-site configuration.
8
+ */
9
+ export declare function shareShopifyCart(cart: Cart): Promise<void>;
@@ -0,0 +1,33 @@
1
+ import { cookies, headers } from "next/headers";
2
+ const SHARED_SHOPIFY_CART_COOKIE_NAME = "replo_shared_cart";
3
+ const SHOPIFY_CART_ID_PREFIX = "gid://shopify/Cart/";
4
+ async function shareShopifyCart(cart) {
5
+ if (process.env.IS_REPLO_SANDBOX || !cart.id.startsWith(SHOPIFY_CART_ID_PREFIX) || !cart.checkoutUrl || !URL.canParse(cart.checkoutUrl)) {
6
+ return;
7
+ }
8
+ const domain = new URL(cart.checkoutUrl).hostname.toLowerCase().replace(/^www\./, "");
9
+ const host = (await headers()).get("host")?.toLowerCase().split(":")[0];
10
+ if (!host?.endsWith(`.${domain}`)) {
11
+ return;
12
+ }
13
+ try {
14
+ (await cookies()).set(
15
+ SHARED_SHOPIFY_CART_COOKIE_NAME,
16
+ cart.id.slice(SHOPIFY_CART_ID_PREFIX.length),
17
+ {
18
+ domain: `.${domain}`,
19
+ path: "/",
20
+ secure: true,
21
+ httpOnly: false,
22
+ sameSite: "lax",
23
+ maxAge: 60 * 60 * 24 * 30
24
+ }
25
+ );
26
+ } catch {
27
+ }
28
+ }
29
+ export {
30
+ SHARED_SHOPIFY_CART_COOKIE_NAME,
31
+ SHOPIFY_CART_ID_PREFIX,
32
+ shareShopifyCart
33
+ };
@@ -29,6 +29,9 @@ export declare class InjectedScript extends Component<InjectedScriptProps> {
29
29
  * over consent site-wide: scripts carry the platform's blocking attributes, its
30
30
  * loader injects after them so its startup scan sees the full set, and Replo's
31
31
  * own gates follow the platform instead of the native banner.
32
+ *
33
+ * A GA4 entry's `runtimeOwner` leaves `gtag.js` to a GTM container or other
34
+ * loader, which must keep loading it; `dataLayer`, config and sink still start.
32
35
  */
33
36
  export declare function ReploScripts({ scripts }: {
34
37
  scripts: ReploScriptEntry[];
@@ -13,7 +13,11 @@ export declare function isXEventIdForPixel({ eventId, pixelId, }: {
13
13
  eventId: string;
14
14
  pixelId: string;
15
15
  }): boolean;
16
- /** The runtime injects these tags in order after consent allows tracking. */
16
+ /**
17
+ * The runtime injects these tags in order after consent allows tracking. A GA4
18
+ * entry with a `runtimeOwner` drops only `gtag.js`; the inline tag still
19
+ * queues the config call for the owner's copy to send.
20
+ */
17
21
  export declare function buildScriptTags(entry: ProviderScriptEntry): ScriptTagDescriptor[];
18
22
  /**
19
23
  * The default consent categories that gate an identifier-provider script when an
@@ -186,6 +186,9 @@ function isXEventIdForPixel({
186
186
  }
187
187
  function buildScriptTags(entry) {
188
188
  const tags = PROVIDER_SNIPPETS[entry.type].buildTags(entry.identifier, entry);
189
+ if (entry.type === "GA4" && entry.runtimeOwner) {
190
+ return tags.filter((tag) => tag.kind !== "external");
191
+ }
189
192
  if (entry.type === "X") {
190
193
  const pageViewId = entry.events?.pageView?.trim();
191
194
  if (pageViewId && isXEventIdForPixel({ eventId: pageViewId, pixelId: entry.identifier })) {
@@ -23,16 +23,36 @@ export type XPixelEvents = {
23
23
  startCheckout?: string;
24
24
  purchase?: string;
25
25
  };
26
+ /**
27
+ * Another loader owns GA4's `gtag.js`. The SDK keeps `dataLayer`, the config
28
+ * call and the analytics sink but never injects `gtag.js`, so if the GTM
29
+ * container (or other code) stops loading it, events queue and never send.
30
+ */
31
+ export type ScriptRuntimeOwner = {
32
+ type: "gtm";
33
+ containerId: string;
34
+ } | {
35
+ type: "external";
36
+ };
26
37
  export type MixpanelRegion = "us" | "eu" | "in";
27
38
  export type ReploScriptEntry = {
28
- type: "GA4" | "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "HubSpot" | "Cookiebot";
39
+ type: "GoogleTagManager" | "Meta" | "TikTok" | "Pinterest" | "Reddit" | "Snapchat" | "Hotjar" | "MicrosoftClarity" | "Contentsquare" | "Segment" | "Northbeam" | "Gorgias" | "Converge" | "HubSpot";
40
+ identifier: string;
41
+ requiredConsent?: ConsentCategory[];
42
+ } | {
43
+ type: "GA4";
29
44
  identifier: string;
30
45
  requiredConsent?: ConsentCategory[];
46
+ runtimeOwner?: ScriptRuntimeOwner;
31
47
  } | {
32
48
  type: "X";
33
49
  identifier: string;
34
50
  events?: XPixelEvents;
35
51
  requiredConsent?: ConsentCategory[];
52
+ } | {
53
+ type: "Cookiebot";
54
+ identifier: string;
55
+ requiredConsent?: ConsentCategory[];
36
56
  } | {
37
57
  type: "Mixpanel";
38
58
  identifier: string;
@@ -42,6 +62,7 @@ export type ReploScriptEntry = {
42
62
  type: "snippet";
43
63
  id: string;
44
64
  body: string;
65
+ module?: boolean;
45
66
  requiredConsent: ConsentCategory[];
46
67
  } | {
47
68
  type: "custom";
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sdk",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "branch": "HEAD",
5
- "commit": "0f7d0830fdc9d0b6c299ee8b9497a749d41aee95",
6
- "builtAt": "2026-10-21T20:51:42.391Z"
5
+ "commit": "f408d4d2ca21640258e7b9a8d20c5a92515cf9d6",
6
+ "builtAt": "2026-10-22T19:30:37.074Z"
7
7
  };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sdk",
3
- "version": "2.3.0",
3
+ "version": "2.5.0",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sdk",
6
- "version": "2.3.0",
6
+ "version": "2.5.0",
7
7
  "branch": "HEAD",
8
- "commit": "0f7d0830fdc9d0b6c299ee8b9497a749d41aee95",
9
- "builtAt": "2026-10-21T20:51:42.391Z"
8
+ "commit": "f408d4d2ca21640258e7b9a8d20c5a92515cf9d6",
9
+ "builtAt": "2026-10-22T19:30:37.074Z"
10
10
  },
11
11
  "description": "Replo SDK — cart, analytics, and data loaders for agent-built Next.js sites.",
12
12
  "license": "SEE LICENSE IN LICENSE",
File without changes