@replohq/sdk 1.9.1 → 1.9.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.
@@ -0,0 +1,10 @@
1
+ export type StorefrontCountryCode = "AC" | "AD" | "AE" | "AF" | "AG" | "AI" | "AL" | "AM" | "AN" | "AO" | "AR" | "AT" | "AU" | "AW" | "AX" | "AZ" | "BA" | "BB" | "BD" | "BE" | "BF" | "BG" | "BH" | "BI" | "BJ" | "BL" | "BM" | "BN" | "BO" | "BQ" | "BR" | "BS" | "BT" | "BV" | "BW" | "BY" | "BZ" | "CA" | "CC" | "CD" | "CF" | "CG" | "CH" | "CI" | "CK" | "CL" | "CM" | "CN" | "CO" | "CR" | "CU" | "CV" | "CW" | "CX" | "CY" | "CZ" | "DE" | "DJ" | "DK" | "DM" | "DO" | "DZ" | "EC" | "EE" | "EG" | "EH" | "ER" | "ES" | "ET" | "FI" | "FJ" | "FK" | "FO" | "FR" | "GA" | "GB" | "GD" | "GE" | "GF" | "GG" | "GH" | "GI" | "GL" | "GM" | "GN" | "GP" | "GQ" | "GR" | "GS" | "GT" | "GW" | "GY" | "HK" | "HM" | "HN" | "HR" | "HT" | "HU" | "ID" | "IE" | "IL" | "IM" | "IN" | "IO" | "IQ" | "IR" | "IS" | "IT" | "JE" | "JM" | "JO" | "JP" | "KE" | "KG" | "KH" | "KI" | "KM" | "KN" | "KP" | "KR" | "KW" | "KY" | "KZ" | "LA" | "LB" | "LC" | "LI" | "LK" | "LR" | "LS" | "LT" | "LU" | "LV" | "LY" | "MA" | "MC" | "MD" | "ME" | "MF" | "MG" | "MK" | "ML" | "MM" | "MN" | "MO" | "MQ" | "MR" | "MS" | "MT" | "MU" | "MV" | "MW" | "MX" | "MY" | "MZ" | "NA" | "NC" | "NE" | "NF" | "NG" | "NI" | "NL" | "NO" | "NP" | "NR" | "NU" | "NZ" | "OM" | "PA" | "PE" | "PF" | "PG" | "PH" | "PK" | "PL" | "PM" | "PN" | "PS" | "PT" | "PY" | "QA" | "RE" | "RO" | "RS" | "RU" | "RW" | "SA" | "SB" | "SC" | "SD" | "SE" | "SG" | "SH" | "SI" | "SJ" | "SK" | "SL" | "SM" | "SN" | "SO" | "SR" | "SS" | "ST" | "SV" | "SX" | "SY" | "SZ" | "TA" | "TC" | "TD" | "TF" | "TG" | "TH" | "TJ" | "TK" | "TL" | "TM" | "TN" | "TO" | "TR" | "TT" | "TV" | "TW" | "TZ" | "UA" | "UG" | "UM" | "US" | "UY" | "UZ" | "VA" | "VC" | "VE" | "VG" | "VN" | "VU" | "WF" | "WS" | "XK" | "YE" | "YT" | "ZA" | "ZM" | "ZW" | "ZZ";
2
+ export type ShopifyMarket = {
3
+ country?: StorefrontCountryCode;
4
+ };
5
+ export type Markets = {
6
+ shopify?: Partial<ShopifyMarket>;
7
+ };
8
+ export type CartBuyerIdentity = {
9
+ country: StorefrontCountryCode;
10
+ };
@@ -21,8 +21,8 @@ export declare const localeRoutingConfigSchema: z.ZodObject<{
21
21
  locales: z.ZodArray<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
22
22
  defaultLocale: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
23
23
  detection: z.ZodDefault<z.ZodEnum<{
24
- language: "language";
25
24
  country: "country";
25
+ language: "language";
26
26
  }>>;
27
27
  urlMode: z.ZodDefault<z.ZodEnum<{
28
28
  prefixed: "prefixed";
@@ -1,3 +1,4 @@
1
+ import type { StorefrontCountryCode } from "../loaders/markets";
1
2
  import type { CartLineAttribute } from "./cart-types";
2
3
  /**
3
4
  * Server action that handles the full buy-now flow:
@@ -19,6 +20,7 @@ export declare function buyNowAction(input: Parameters<typeof buyNowWithRecovery
19
20
  }>;
20
21
  export declare function buyNowWithRecoveryAction(input: {
21
22
  checkoutAttemptId?: string;
23
+ country?: StorefrontCountryCode;
22
24
  lineItems: {
23
25
  variantId: string;
24
26
  quantity: number;
@@ -114,6 +114,7 @@ async function buyNowWithRecoveryAction(input) {
114
114
  const cart = await createCartAction({
115
115
  lines: cartLines,
116
116
  skipStoringCart: true,
117
+ country: input.country,
117
118
  discountCodes: discountCodes.length > 0 ? discountCodes : void 0
118
119
  });
119
120
  if (!cart?.checkoutUrl) {
@@ -1,3 +1,4 @@
1
+ import type { StorefrontCountryCode } from "../loaders/markets";
1
2
  import type { AddToCartPayload, Cart, CartLinePayload, RemoveFromCartPayload, UpdateCartLineItemPayload } from "./cart-types";
2
3
  /**
3
4
  * Gets the existing cart or creates a new one if none exists. This is necessary
@@ -7,7 +8,9 @@ import type { AddToCartPayload, Cart, CartLinePayload, RemoveFromCartPayload, Up
7
8
  *
8
9
  * @returns The existing cart or a newly created cart
9
10
  */
10
- export declare function getOrCreateCartAction(): Promise<Cart | null>;
11
+ export declare function getOrCreateCartAction(options?: {
12
+ country?: StorefrontCountryCode;
13
+ }): Promise<Cart | null>;
11
14
  /**
12
15
  * Retrieves the current cart from cookies. Returns null if no cart exists or
13
16
  * if the cart has expired/been deleted.
@@ -28,6 +31,7 @@ export declare function createCartAction(options?: {
28
31
  lines?: CartLinePayload[];
29
32
  skipStoringCart?: boolean;
30
33
  discountCodes?: string[];
34
+ country?: StorefrontCountryCode;
31
35
  }): Promise<Cart | null>;
32
36
  /**
33
37
  * Adds line items to an existing cart.
@@ -11,13 +11,20 @@ import {
11
11
  saveCartToCookie
12
12
  } from "./utils/cookie-cart-persistence";
13
13
  const SHOPIFY_CART_COOKIE_NAME = "replo_cart_id";
14
- async function getOrCreateCartAction() {
14
+ async function getOrCreateCartAction(options) {
15
15
  const existingCart = await getCartAction();
16
16
  if (existingCart) {
17
17
  const cartGateway = await getCartGateway();
18
- return await cartGateway.reconcileStoredCart(existingCart);
18
+ const reconciledCart = await cartGateway.reconcileStoredCart(existingCart);
19
+ if (options?.country && reconciledCart.buyerIdentity?.country !== options.country) {
20
+ return await cartGateway.updateCartBuyerIdentity({
21
+ cartId: reconciledCart.id,
22
+ buyerIdentity: { country: options.country }
23
+ });
24
+ }
25
+ return reconciledCart;
19
26
  }
20
- return await createCartAction();
27
+ return await createCartAction({ country: options?.country });
21
28
  }
22
29
  async function getCartAction() {
23
30
  const checkoutProvider = await getCheckoutProvider();
@@ -44,10 +51,19 @@ async function getCartAction() {
44
51
  return cart;
45
52
  }
46
53
  async function createCartAction(options) {
47
- const { lines, skipStoringCart = false, discountCodes } = options ?? {};
54
+ const {
55
+ lines,
56
+ skipStoringCart = false,
57
+ discountCodes,
58
+ country
59
+ } = options ?? {};
48
60
  const checkoutProvider = await getCheckoutProvider();
49
61
  const cartGateway = await getCartGateway();
50
- const cart = await cartGateway.createCart(lines, discountCodes);
62
+ const cart = await cartGateway.createCart({
63
+ lines,
64
+ discountCodes,
65
+ buyerIdentity: country ? { country } : void 0
66
+ });
51
67
  if (cart && !skipStoringCart) {
52
68
  try {
53
69
  if (checkoutProvider === "stripe") {
@@ -0,0 +1,2 @@
1
+ import type { StorefrontCountryCode } from "../loaders/markets";
2
+ export declare const CartCountryContext: import("react").Context<StorefrontCountryCode | undefined>;
@@ -0,0 +1,6 @@
1
+ "use client";
2
+ import { createContext } from "react";
3
+ const CartCountryContext = createContext(void 0);
4
+ export {
5
+ CartCountryContext
6
+ };
@@ -1,3 +1,4 @@
1
+ import type { StorefrontCountryCode } from "../loaders/markets";
1
2
  /**
2
3
  * Server component that loads the cart and provides it to child components
3
4
  * through the CartProvider context. In development mode, skips initial
@@ -6,4 +7,6 @@
6
7
  * @param children - Child components that will have access to the cart
7
8
  * @returns A CartProvider component wrapping the children with cart data
8
9
  */
9
- export declare function CartLoader({ children }: React.PropsWithChildren): Promise<import("react/jsx-runtime").JSX.Element>;
10
+ export declare function CartLoader({ children, country, }: React.PropsWithChildren<{
11
+ country?: StorefrontCountryCode;
12
+ }>): Promise<import("react/jsx-runtime").JSX.Element>;
@@ -2,13 +2,16 @@ import { jsx } from "react/jsx-runtime";
2
2
  import { unstable_rethrow } from "next/navigation";
3
3
  import { getCartAction } from "./cart-actions";
4
4
  import { CartProvider } from "./cart-provider";
5
- async function CartLoader({ children }) {
5
+ async function CartLoader({
6
+ children,
7
+ country
8
+ }) {
6
9
  const cart = process.env.NODE_ENV === "development" ? null : await getCartAction().catch((error) => {
7
10
  unstable_rethrow(error);
8
11
  console.warn("[Replo] Failed to load cart:", error);
9
12
  return null;
10
13
  });
11
- return /* @__PURE__ */ jsx(CartProvider, { cart, children });
14
+ return /* @__PURE__ */ jsx(CartProvider, { cart, country, children });
12
15
  }
13
16
  export {
14
17
  CartLoader
@@ -1,3 +1,4 @@
1
+ import type { StorefrontCountryCode } from "../loaders/markets";
1
2
  import type { Cart, CartLine, CartLineAdjustment, CartLinePayload } from "./cart-types";
2
3
  import React from "react";
3
4
  export type CartOperation = "add" | "update" | "remove" | "discount";
@@ -93,9 +94,10 @@ export interface CartContextType {
93
94
  *
94
95
  * @returns A context provider wrapping the children with cart functionality
95
96
  */
96
- export declare function CartProvider({ children, cart, }: React.PropsWithChildren<{
97
+ export declare function CartProvider({ children, cart, country, }: React.PropsWithChildren<{
97
98
  /** Initial cart data from the server (can be null) */
98
99
  cart: Cart | null;
100
+ country?: StorefrontCountryCode;
99
101
  }>): import("react/jsx-runtime").JSX.Element;
100
102
  /**
101
103
  * @deprecated For internal use only. Use useAddToCart or useBuyNow
@@ -12,6 +12,7 @@ import {
12
12
  updateCartDiscountCodesAction,
13
13
  updateCartLineItemAction
14
14
  } from "./cart-actions";
15
+ import { CartCountryContext } from "./cart-country-context";
15
16
  import {
16
17
  deriveAddToCartAdjustments,
17
18
  deriveUpdateCartAdjustments
@@ -24,18 +25,27 @@ import { CartContextError, toCartError } from "./utils/cart-error";
24
25
  import { recalculateCartCost } from "./utils/cart-utils";
25
26
  import { createOptimisticSellingPlanAllocation } from "./utils/variant-to-cart-line";
26
27
  let cartCreationPromise = null;
27
- function getOrCreateSharedCart() {
28
- cartCreationPromise ??= getOrCreateCartAction().then((cart) => {
28
+ let cartCreationCountry;
29
+ function getOrCreateSharedCart(country) {
30
+ if (cartCreationPromise && cartCreationCountry === country) {
31
+ return cartCreationPromise;
32
+ }
33
+ const previousCreation = cartCreationPromise;
34
+ const creation = (async () => {
35
+ if (previousCreation) await previousCreation.catch(() => null);
36
+ const cart = await getOrCreateCartAction({ country });
29
37
  if (!cart) {
30
38
  throw new CartContextError({
31
39
  message: "Your cart could not be loaded. Please try again."
32
40
  });
33
41
  }
34
42
  return cart;
35
- }).finally(() => {
36
- cartCreationPromise = null;
43
+ })().finally(() => {
44
+ if (cartCreationPromise === creation) cartCreationPromise = null;
37
45
  });
38
- return cartCreationPromise;
46
+ cartCreationCountry = country;
47
+ cartCreationPromise = creation;
48
+ return creation;
39
49
  }
40
50
  function addLineToCart({
41
51
  cart,
@@ -84,7 +94,8 @@ const CartContext = React.createContext(
84
94
  );
85
95
  function CartProvider({
86
96
  children,
87
- cart
97
+ cart,
98
+ country
88
99
  }) {
89
100
  const [cartData, setCartData] = React.useState(cart);
90
101
  const optimisticCart = React.useRef(cart);
@@ -101,6 +112,10 @@ function CartProvider({
101
112
  const [error, setError] = React.useState(null);
102
113
  const clearError = React.useCallback(() => setError(null), []);
103
114
  const isMounted = React.useRef(true);
115
+ const marketSelection = React.useRef({ country });
116
+ if (marketSelection.current.country !== country) {
117
+ marketSelection.current = { country };
118
+ }
104
119
  const runWithErrorState = async (operation, mutate) => {
105
120
  setError(null);
106
121
  try {
@@ -175,18 +190,22 @@ function CartProvider({
175
190
  });
176
191
  };
177
192
  const initializeCart = React.useCallback(async () => {
178
- const loadedCart = await getOrCreateSharedCart();
179
- if (!isMounted.current) {
180
- throw new DOMException("Cart provider was unmounted", "AbortError");
193
+ const selection = marketSelection.current;
194
+ const loadedCart = await getOrCreateSharedCart(country);
195
+ if (!isMounted.current || marketSelection.current !== selection) {
196
+ throw new DOMException(
197
+ "Cart initialization was superseded",
198
+ "AbortError"
199
+ );
181
200
  }
182
201
  serverCart.current = loadedCart;
183
202
  if (pendingMutations.current === 0) updateCartData(loadedCart);
184
203
  return loadedCart;
185
- }, [updateCartData]);
204
+ }, [country, updateCartData]);
186
205
  React.useEffect(() => {
187
206
  isMounted.current = true;
188
207
  void initializeCart().catch((error2) => {
189
- if (isMounted.current) {
208
+ if (isMounted.current && !(error2 instanceof DOMException && error2.name === "AbortError")) {
190
209
  console.error("[Replo] Error getting or creating cart:", error2);
191
210
  }
192
211
  });
@@ -400,7 +419,8 @@ function CartProvider({
400
419
  const cart2 = await createCartAction({
401
420
  lines,
402
421
  skipStoringCart: true,
403
- discountCodes
422
+ discountCodes,
423
+ country
404
424
  });
405
425
  if (cart2) {
406
426
  return cart2;
@@ -412,7 +432,7 @@ function CartProvider({
412
432
  }
413
433
  };
414
434
  const checkoutUrl = cartData?.checkoutUrl ?? "/";
415
- return /* @__PURE__ */ jsx(
435
+ return /* @__PURE__ */ jsx(CartCountryContext.Provider, { value: country, children: /* @__PURE__ */ jsx(
416
436
  CartContext.Provider,
417
437
  {
418
438
  value: {
@@ -436,7 +456,7 @@ function CartProvider({
436
456
  },
437
457
  children
438
458
  }
439
- );
459
+ ) });
440
460
  }
441
461
  function useCartInternal() {
442
462
  const context = React.useContext(CartContext);
@@ -74,5 +74,8 @@ export declare const cartSchema: z.ZodObject<{
74
74
  discountedAmount: z.ZodNumber;
75
75
  title: z.ZodNullable<z.ZodString>;
76
76
  }, z.core.$strip>>>;
77
+ buyerIdentity: z.ZodOptional<z.ZodObject<{
78
+ country: z.ZodNullable<z.ZodString>;
79
+ }, z.core.$strip>>;
77
80
  }, z.core.$strip>;
78
81
  export type Cart = z.infer<typeof cartSchema>;
@@ -66,7 +66,8 @@ const cartSchema = z.object({
66
66
  cost: cartCostSchema,
67
67
  checkoutUrl: z.string().optional(),
68
68
  discountCodes: z.array(cartDiscountCodeSchema).optional(),
69
- discountAllocations: z.array(cartDiscountAllocationSchema).optional()
69
+ discountAllocations: z.array(cartDiscountAllocationSchema).optional(),
70
+ buyerIdentity: z.object({ country: z.string().nullable() }).optional()
70
71
  });
71
72
  export {
72
73
  cartSchema
@@ -3,6 +3,7 @@
3
3
  * @module
4
4
  */
5
5
  import type { SellingPlanGroup } from "../loaders/loader-schemas";
6
+ import type { StorefrontCountryCode } from "../loaders/markets";
6
7
  export interface Image {
7
8
  url: string;
8
9
  altText: string | null;
@@ -118,6 +119,14 @@ export interface Cart {
118
119
  checkoutUrl?: string;
119
120
  discountCodes?: CartDiscountCode[];
120
121
  discountAllocations?: CartDiscountAllocation[];
122
+ /**
123
+ * The market Shopify has the cart in. Shopify prices `cost` and the checkout
124
+ * from this, not from the catalog country the product loaders pass, so it is
125
+ * what tells a market site whether the cart already matches the page.
126
+ */
127
+ buyerIdentity?: {
128
+ country: string | null;
129
+ };
121
130
  }
122
131
  /**
123
132
  * How a cart write differed from what the shopper asked for, derived by
@@ -157,3 +166,6 @@ export interface UpdateCartDiscountCodesPayload {
157
166
  cartId: string;
158
167
  discountCodes: string[];
159
168
  }
169
+ export interface CartBuyerIdentity {
170
+ country: StorefrontCountryCode;
171
+ }
@@ -1,4 +1,4 @@
1
- import type { AddToCartPayload, Cart, CartLinePayload, RemoveFromCartPayload, UpdateCartDiscountCodesPayload, UpdateCartLineItemPayload } from "../cart-types";
1
+ import type { AddToCartPayload, Cart, CartBuyerIdentity, CartLinePayload, RemoveFromCartPayload, UpdateCartDiscountCodesPayload, UpdateCartLineItemPayload } from "../cart-types";
2
2
  export interface CartGateway {
3
3
  getCart(cartId: string): Promise<Cart | null>;
4
4
  /**
@@ -8,8 +8,16 @@ export interface CartGateway {
8
8
  * cart unchanged.
9
9
  */
10
10
  reconcileStoredCart(cart: Cart): Promise<Cart>;
11
- createCart(lines?: CartLinePayload[], discountCodes?: string[]): Promise<Cart | null>;
11
+ createCart(options?: {
12
+ lines?: CartLinePayload[];
13
+ discountCodes?: string[];
14
+ buyerIdentity?: CartBuyerIdentity;
15
+ }): Promise<Cart | null>;
12
16
  addToCart(cartPayload: AddToCartPayload): Promise<Cart | null>;
17
+ updateCartBuyerIdentity(payload: {
18
+ cartId: string;
19
+ buyerIdentity: CartBuyerIdentity;
20
+ }): Promise<Cart | null>;
13
21
  updateCartLineItem(cartPayload: UpdateCartLineItemPayload): Promise<Cart | null>;
14
22
  removeCartLineItems(cartPayload: RemoveFromCartPayload): Promise<Cart | null>;
15
23
  updateCartDiscountCodes(cartPayload: UpdateCartDiscountCodesPayload): Promise<Cart | null>;
@@ -1,5 +1,5 @@
1
1
  import type { ReconciledCartLine } from "../../gateways/canopy-api";
2
- import type { AddToCartPayload, Cart, CartLinePayload, RemoveFromCartPayload, UpdateCartDiscountCodesPayload, UpdateCartLineItemPayload } from "../cart-types";
2
+ import type { AddToCartPayload, Cart, CartBuyerIdentity, CartLinePayload, RemoveFromCartPayload, UpdateCartDiscountCodesPayload, UpdateCartLineItemPayload } from "../cart-types";
3
3
  import type { LineMergeFn } from "../utils/cookie-cart-persistence";
4
4
  import type { CartGateway } from "./cart-gateway";
5
5
  /**
@@ -28,10 +28,18 @@ export declare abstract class CookieBasedCartGateway implements CartGateway {
28
28
  private reconcileForWrite;
29
29
  reconcileStoredCart(cart: Cart): Promise<Cart>;
30
30
  getCart(cartId: string): Promise<Cart | null>;
31
- createCart(lines?: CartLinePayload[], _discountCodes?: string[]): Promise<Cart | null>;
31
+ createCart({ lines, }?: {
32
+ lines?: CartLinePayload[];
33
+ discountCodes?: string[];
34
+ buyerIdentity?: CartBuyerIdentity;
35
+ }): Promise<Cart | null>;
32
36
  addToCart(cartPayload: AddToCartPayload): Promise<Cart | null>;
33
37
  updateCartLineItem(cartPayload: UpdateCartLineItemPayload): Promise<Cart | null>;
34
38
  removeCartLineItems(cartPayload: RemoveFromCartPayload): Promise<Cart | null>;
35
39
  updateCartDiscountCodes(cartPayload: UpdateCartDiscountCodesPayload): Promise<Cart | null>;
40
+ updateCartBuyerIdentity({ cartId, }: {
41
+ cartId: string;
42
+ buyerIdentity: CartBuyerIdentity;
43
+ }): Promise<Cart | null>;
36
44
  }
37
45
  export {};
@@ -62,7 +62,9 @@ class CookieBasedCartGateway {
62
62
  return null;
63
63
  }
64
64
  }
65
- async createCart(lines, _discountCodes) {
65
+ async createCart({
66
+ lines
67
+ } = {}) {
66
68
  try {
67
69
  const cart = await createCartWithPersistence({
68
70
  cookieName: this.config.cookieName,
@@ -149,6 +151,11 @@ class CookieBasedCartGateway {
149
151
  return null;
150
152
  }
151
153
  }
154
+ async updateCartBuyerIdentity({
155
+ cartId
156
+ }) {
157
+ return this.getCart(cartId);
158
+ }
152
159
  }
153
160
  export {
154
161
  CookieBasedCartGateway
@@ -8,7 +8,11 @@ class ResolverCartGateway {
8
8
  const gateway = await getCanopyApiGateway();
9
9
  return await gateway.cartGet({ cartId });
10
10
  }
11
- async createCart(lines, discountCodes) {
11
+ async createCart({
12
+ lines,
13
+ discountCodes,
14
+ buyerIdentity
15
+ } = {}) {
12
16
  try {
13
17
  const gateway = await getCanopyApiGateway();
14
18
  return await gateway.cartCreate({
@@ -21,7 +25,8 @@ class ResolverCartGateway {
21
25
  })),
22
26
  sellingPlanId: line.sellingPlanId
23
27
  })),
24
- discountCodes
28
+ discountCodes,
29
+ country: buyerIdentity?.country
25
30
  });
26
31
  } catch (error) {
27
32
  console.warn("Failed to create cart via canopy-api:", error);
@@ -94,6 +99,24 @@ class ResolverCartGateway {
94
99
  return null;
95
100
  }
96
101
  }
102
+ async updateCartBuyerIdentity({
103
+ cartId,
104
+ buyerIdentity
105
+ }) {
106
+ try {
107
+ const gateway = await getCanopyApiGateway();
108
+ return await gateway.cartUpdateBuyerIdentity({
109
+ cartId,
110
+ country: buyerIdentity.country
111
+ });
112
+ } catch (error) {
113
+ console.warn(
114
+ "Failed to update cart buyer identity via canopy-api:",
115
+ error
116
+ );
117
+ return null;
118
+ }
119
+ }
97
120
  }
98
121
  async function createResolverCartGateway() {
99
122
  return new ResolverCartGateway();
@@ -4,6 +4,7 @@ import { useClientValue } from "../../hooks/use-client-value";
4
4
  import { CanopyError } from "../../lib/canopy-error";
5
5
  import { navigateExternal } from "../../lib/external-navigation";
6
6
  import { buyNowWithRecoveryAction } from "../buy-now-action";
7
+ import { CartCountryContext } from "../cart-country-context";
7
8
  const REPLO_LOCAL_STORAGE_SESSION_KEY = "_replo_sid";
8
9
  const REPLO_SESSION_ID_KEY = "rsid";
9
10
  const REPLO_CLIENT_ID_KEY = "rclid";
@@ -13,6 +14,7 @@ class CheckoutAttemptError extends CanopyError {
13
14
  }
14
15
  const checkoutAttempts = /* @__PURE__ */ new Map();
15
16
  function useBuyNow() {
17
+ const country = React.useContext(CartCountryContext);
16
18
  const reploSessionString = useClientValue(
17
19
  () => localStorage.getItem(REPLO_LOCAL_STORAGE_SESSION_KEY),
18
20
  null
@@ -25,6 +27,7 @@ function useBuyNow() {
25
27
  });
26
28
  }
27
29
  const checkoutInput = {
30
+ country,
28
31
  lineItems: variants.map((variant) => ({
29
32
  variantId: variant.variantId,
30
33
  quantity: variant.quantity ?? 1,
@@ -82,7 +85,7 @@ function useBuyNow() {
82
85
  navigateExternal(url.toString());
83
86
  return { result: "redirected" };
84
87
  },
85
- [reploSessionString]
88
+ [reploSessionString, country]
86
89
  );
87
90
  return { buyNow };
88
91
  }
@@ -1,4 +1,5 @@
1
1
  import type { Cart } from "../cart/cart-schema";
2
+ import type { StorefrontCountryCode } from "../loaders/markets";
2
3
  import { z } from "zod";
3
4
  import { CanopyError } from "../lib/canopy-error";
4
5
  type CanopyApiGatewayErrorConfig = {
@@ -71,6 +72,7 @@ declare class CanopyApiGateway {
71
72
  sellingPlanId?: string | null;
72
73
  }[];
73
74
  discountCodes?: string[];
75
+ country?: StorefrontCountryCode;
74
76
  }): Promise<Cart>;
75
77
  cartAddLines({ cartId, lines, }: {
76
78
  cartId: string;
@@ -104,6 +106,10 @@ declare class CanopyApiGateway {
104
106
  cartId: string;
105
107
  discountCodes: string[];
106
108
  }): Promise<Cart>;
109
+ cartUpdateBuyerIdentity({ cartId, country, }: {
110
+ cartId: string;
111
+ country: StorefrontCountryCode;
112
+ }): Promise<Cart>;
107
113
  }
108
114
  export declare function getCanopyApiGateway(): Promise<CanopyApiGateway>;
109
115
  export {};
@@ -194,7 +194,8 @@ If you're running locally, make sure the canopy-api cloudflare worker is running
194
194
  async cartCreate(options) {
195
195
  return this._cartPost("/create", {
196
196
  lines: options?.lines,
197
- discountCodes: options?.discountCodes
197
+ discountCodes: options?.discountCodes,
198
+ country: options?.country
198
199
  });
199
200
  }
200
201
  async cartAddLines({
@@ -221,6 +222,12 @@ If you're running locally, make sure the canopy-api cloudflare worker is running
221
222
  }) {
222
223
  return this._cartPost("/update-discount-codes", { cartId, discountCodes });
223
224
  }
225
+ async cartUpdateBuyerIdentity({
226
+ cartId,
227
+ country
228
+ }) {
229
+ return this._cartPost("/update-buyer-identity", { cartId, country });
230
+ }
224
231
  }
225
232
  async function getCanopyApiGateway() {
226
233
  return CanopyApiGateway.setup();
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sdk",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "branch": "HEAD",
5
- "commit": "dadbeffde02243998e85250ba4b2a21565c0b804",
6
- "builtAt": "2026-09-17T18:18:44.111Z"
5
+ "commit": "b00423087f2b01a9cc8fb4700d453161b9ec846c",
6
+ "builtAt": "2026-09-17T18:32:43.736Z"
7
7
  };
@@ -1,8 +1,9 @@
1
+ import type { StorefrontCountryCode } from "./markets";
1
2
  export type CollectionLoaderArgs = {
2
3
  handle?: string;
3
4
  collectionGid?: string;
4
5
  language?: string;
5
- country?: string;
6
+ country?: StorefrontCountryCode;
6
7
  };
7
8
  /**
8
9
  * Builds the single object used as BOTH the React Query key and the loader
@@ -1,9 +1,10 @@
1
+ import type { StorefrontCountryCode } from "./markets";
1
2
  export type CollectionProductsLoaderArgs = {
2
3
  collectionGid: string;
3
4
  first?: number;
4
5
  after?: string;
5
6
  language?: string;
6
- country?: string;
7
+ country?: StorefrontCountryCode;
7
8
  };
8
9
  /**
9
10
  * Builds the single object used as BOTH the React Query key and the loader
@@ -0,0 +1 @@
1
+ export type { Markets, ShopifyMarket, StorefrontCountryCode, } from "../_vendor/schemas/generated/storefrontCountryCode";
File without changes
@@ -1,3 +1,4 @@
1
+ import type { StorefrontCountryCode } from "./markets";
1
2
  export type MetafieldIdentifier = {
2
3
  namespace: string;
3
4
  key: string;
@@ -8,7 +9,7 @@ export type ProductLoaderArgs = {
8
9
  productMetafieldIdentifiers?: MetafieldIdentifier[];
9
10
  variantMetafieldIdentifiers?: MetafieldIdentifier[];
10
11
  language?: string;
11
- country?: string;
12
+ country?: StorefrontCountryCode;
12
13
  };
13
14
  /**
14
15
  * Builds the single object used as BOTH the React Query key and the loader
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sdk",
3
- "version": "1.9.1",
3
+ "version": "1.9.2",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sdk",
6
- "version": "1.9.1",
6
+ "version": "1.9.2",
7
7
  "branch": "HEAD",
8
- "commit": "dadbeffde02243998e85250ba4b2a21565c0b804",
9
- "builtAt": "2026-09-17T18:18:44.111Z"
8
+ "commit": "b00423087f2b01a9cc8fb4700d453161b9ec846c",
9
+ "builtAt": "2026-09-17T18:32:43.736Z"
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",
@@ -213,6 +213,10 @@
213
213
  "types": "./loaders/collection-products-loader-args.d.ts",
214
214
  "default": "./loaders/collection-products-loader-args.js"
215
215
  },
216
+ "./loaders/markets": {
217
+ "types": "./loaders/markets.d.ts",
218
+ "default": "./loaders/markets.js"
219
+ },
216
220
  "./orders/order-status": {
217
221
  "types": "./orders/order-status.d.ts",
218
222
  "default": "./orders/order-status.js"
@@ -1,3 +1,4 @@
1
+ import type { Markets } from "../loaders/markets";
1
2
  /**
2
3
  * Root provider for a Replo site. Mount it once in `app/layout.tsx`, wrapping
3
4
  * the whole app. It sets up the React Query client every data loader reads
@@ -17,4 +18,7 @@
17
18
  * }
18
19
  * ```
19
20
  */
20
- export declare function ReploProvider({ children }: React.PropsWithChildren): Promise<import("react/jsx-runtime").JSX.Element>;
21
+ export declare function ReploProvider({ children, markets, }: React.PropsWithChildren<{
22
+ /** Nested so Shopify and later Replo-product markets do not share one `country`. */
23
+ markets?: Markets;
24
+ }>): Promise<import("react/jsx-runtime").JSX.Element>;
@@ -6,7 +6,10 @@ import { getCheckoutProvider } from "../lib/integration-utils";
6
6
  import { LoaderErrorDetailsProvider } from "../loaders/loader-error-boundary";
7
7
  import { shouldShowLoaderErrorDetails } from "../loaders/loader-error-details";
8
8
  import { QueryProvider } from "./query-provider";
9
- async function ReploProvider({ children }) {
9
+ async function ReploProvider({
10
+ children,
11
+ markets
12
+ }) {
10
13
  let checkoutProvider = "none";
11
14
  try {
12
15
  checkoutProvider = await getCheckoutProvider();
@@ -15,7 +18,7 @@ async function ReploProvider({ children }) {
15
18
  }
16
19
  return /* @__PURE__ */ jsx(QueryProvider, { children: /* @__PURE__ */ jsx(LoaderErrorDetailsProvider, { enabled: shouldShowLoaderErrorDetails(), children: /* @__PURE__ */ jsxs(AnalyticsProvider, { checkoutProvider, children: [
17
20
  /* @__PURE__ */ jsx(ReploFirstPartyPixel, {}),
18
- /* @__PURE__ */ jsx(CartLoader, { children })
21
+ /* @__PURE__ */ jsx(CartLoader, { country: markets?.shopify?.country, children })
19
22
  ] }) }) });
20
23
  }
21
24
  export {