@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.
- package/_vendor/replo-utils/lib/crypto.d.ts +1 -0
- package/_vendor/schemas/generated/consent.d.ts +12 -1
- package/_vendor/schemas/moneyFormat.mjs +1 -1
- package/_vendor/schemas/routing/locale.mjs +1 -1
- package/_vendor/schemas/routing/rules.mjs +1 -1
- package/cart/cart-actions.js +21 -1
- package/cart/utils/shopify-shared-cart.d.ts +9 -0
- package/cart/utils/shopify-shared-cart.js +33 -0
- package/consent/replo-scripts.d.ts +3 -0
- package/consent/script-snippets.d.ts +5 -1
- package/consent/script-snippets.js +3 -0
- package/consent/types.d.ts +22 -1
- package/lib/buildMetadata.js +3 -3
- package/package.json +4 -4
- /package/{chunk-CKRRL66R.mjs → chunk-AE5H7AJS.mjs} +0 -0
|
@@ -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: "
|
|
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,5 +1,5 @@
|
|
|
1
1
|
import '../../../chunk-XSMYSAKY.mjs';
|
|
2
|
-
import { ReploError } from '../../../chunk-
|
|
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-
|
|
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';
|
package/cart/cart-actions.js
CHANGED
|
@@ -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
|
-
/**
|
|
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 })) {
|
package/consent/types.d.ts
CHANGED
|
@@ -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: "
|
|
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";
|
package/lib/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-10-
|
|
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
|
+
"version": "2.5.0",
|
|
4
4
|
"reploBuild": {
|
|
5
5
|
"packageName": "@replohq/sdk",
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.5.0",
|
|
7
7
|
"branch": "HEAD",
|
|
8
|
-
"commit": "
|
|
9
|
-
"builtAt": "2026-10-
|
|
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
|