@replohq/sdk 2.4.0 → 2.7.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.
Files changed (53) hide show
  1. package/_vendor/replo-utils/lib/crypto.d.ts +1 -0
  2. package/_vendor/replo-utils/lib/json.mjs +4 -4
  3. package/_vendor/replo-utils/lib/misc.mjs +726 -4
  4. package/_vendor/replo-utils/lib/object.mjs +3 -3
  5. package/_vendor/schemas/contentCollection.d.ts +2 -2
  6. package/_vendor/schemas/contentEntry.mjs +3 -3
  7. package/_vendor/schemas/contentManifest.mjs +6 -6
  8. package/_vendor/schemas/moneyFormat.mjs +1 -1
  9. package/_vendor/schemas/routing/locale.d.ts +1 -1
  10. package/_vendor/schemas/routing/locale.mjs +1 -6
  11. package/_vendor/schemas/routing/rules.d.ts +1 -1
  12. package/_vendor/schemas/routing/rules.mjs +1 -6
  13. package/analytics/pixel-meta.d.ts +14 -0
  14. package/analytics/pixel-meta.js +28 -0
  15. package/analytics/replo-first-party-pixel.d.ts +5 -4
  16. package/analytics/replo-first-party-pixel.js +7 -3
  17. package/analytics/replo-pixel-from-meta.d.ts +7 -0
  18. package/analytics/replo-pixel-from-meta.js +27 -0
  19. package/analytics/replo-pixel-script.d.ts +3 -1
  20. package/analytics/replo-pixel-script.js +4 -2
  21. package/cart/buy-now-action.d.ts +2 -13
  22. package/cart/buy-now-action.js +11 -75
  23. package/cart/cart-actions.js +21 -1
  24. package/cart/checkout-actions.d.ts +34 -0
  25. package/cart/checkout-actions.js +27 -0
  26. package/cart/checkout-appearance.d.ts +35 -0
  27. package/cart/checkout-appearance.js +131 -0
  28. package/cart/checkout-session-actions.d.ts +20 -0
  29. package/cart/checkout-session-actions.js +12 -0
  30. package/cart/checkout.d.ts +110 -0
  31. package/cart/checkout.js +420 -0
  32. package/cart/start-stripe-checkout.d.ts +40 -0
  33. package/cart/start-stripe-checkout.js +75 -0
  34. package/cart/utils/shopify-shared-cart.d.ts +9 -0
  35. package/cart/utils/shopify-shared-cart.js +33 -0
  36. package/{chunk-TTEBDCWT.mjs → chunk-4YTXURVN.mjs} +2 -2
  37. package/{chunk-O4QBR4QC.mjs → chunk-DMJ5VUD6.mjs} +2 -2
  38. package/{chunk-OPKMOW43.mjs → chunk-GNIXARC7.mjs} +1 -1
  39. package/{chunk-XWWFRIXV.mjs → chunk-N4WC2Z43.mjs} +1 -1
  40. package/{chunk-PSYP2DYM.mjs → chunk-QYGRVXWQ.mjs} +1 -1
  41. package/env.d.ts +1 -0
  42. package/env.js +1 -0
  43. package/gateways/canopy-api.d.ts +97 -0
  44. package/gateways/canopy-api.js +120 -0
  45. package/lib/buildMetadata.js +3 -3
  46. package/package.json +14 -4
  47. package/_vendor/replo-utils/lib/errors.d.ts +0 -84
  48. package/_vendor/schemas/errors.d.ts +0 -450
  49. package/_vendor/schemas/generated/errors.d.ts +0 -64
  50. package/chunk-XGXJOHUY.mjs +0 -724
  51. package/chunk-XSMYSAKY.mjs +0 -78
  52. /package/{chunk-CKRRL66R.mjs → chunk-AE5H7AJS.mjs} +0 -0
  53. /package/{chunk-ZPDXVFCR.mjs → chunk-JBCOJHZN.mjs} +0 -0
@@ -0,0 +1,40 @@
1
+ import type { CartLineAttribute } from "./cart-types";
2
+ import { CanopyError } from "../lib/canopy-error";
3
+ export declare class StripeCheckoutError extends CanopyError {
4
+ }
5
+ export type StripeCheckoutOutcome = {
6
+ result: "created";
7
+ redirectUrl: string;
8
+ } | {
9
+ result: "restartCheckout";
10
+ message: string;
11
+ } | {
12
+ result: "outOfStock";
13
+ variants: {
14
+ variantId: string;
15
+ available: number;
16
+ }[];
17
+ };
18
+ /**
19
+ * Opens a Stripe Checkout session for a cart: Stripe's hosted page, or the
20
+ * site's own `paymentPagePath` page (built on `<CheckoutSessionProvider>`) with
21
+ * the session in the URL fragment. Canopy answers an embedded request with a
22
+ * hosted session where it can't serve embedded checkout.
23
+ */
24
+ export declare function startStripeCheckout({ checkoutAttemptId, lineItems, discountCodes, pageUrl, ui, }: {
25
+ checkoutAttemptId: string | null;
26
+ lineItems: {
27
+ variantId: string;
28
+ quantity: number;
29
+ sellingPlanId: string | null;
30
+ properties: CartLineAttribute[];
31
+ }[];
32
+ discountCodes: string[];
33
+ pageUrl: string;
34
+ ui: {
35
+ mode: "hosted";
36
+ } | {
37
+ mode: "form";
38
+ paymentPagePath: string;
39
+ };
40
+ }): Promise<StripeCheckoutOutcome>;
@@ -0,0 +1,75 @@
1
+ import { headers } from "next/headers";
2
+ import { v4 as uuidv4 } from "uuid";
3
+ import { getCanopyApiGateway } from "../gateways/canopy-api";
4
+ import { CanopyError } from "../lib/canopy-error";
5
+ import { REPLO_ATTRIBUTION_PROPERTY } from "./cart-types";
6
+ import { getReturnUrls } from "./return-urls";
7
+ class StripeCheckoutError extends CanopyError {
8
+ }
9
+ async function startStripeCheckout({
10
+ checkoutAttemptId,
11
+ lineItems,
12
+ discountCodes,
13
+ pageUrl,
14
+ ui
15
+ }) {
16
+ const headersList = await headers();
17
+ const host = headersList.get("host");
18
+ if (!host) {
19
+ throw new StripeCheckoutError({ message: "Missing host header" });
20
+ }
21
+ const { origin, cancelUrl } = getReturnUrls({
22
+ host,
23
+ forwardedHost: headersList.get("x-forwarded-host"),
24
+ forwardedProto: headersList.get("x-forwarded-proto"),
25
+ requestOrigin: headersList.get("origin"),
26
+ pageUrl
27
+ });
28
+ const checkoutPage = ui.mode === "form" ? new URL(ui.paymentPagePath, origin) : null;
29
+ if (checkoutPage && checkoutPage.origin !== origin) {
30
+ throw new StripeCheckoutError({
31
+ message: "paymentPagePath must be a path on this site",
32
+ additionalData: { paymentPagePath: checkoutPage.toString() }
33
+ });
34
+ }
35
+ if (discountCodes.length > 0 || lineItems.some((lineItem) => Boolean(lineItem.sellingPlanId))) {
36
+ console.warn(
37
+ "[Replo] Stripe buy now does not support selling plans or discount codes; those options were ignored."
38
+ );
39
+ }
40
+ const outcome = await (await getCanopyApiGateway()).createStripeCheckoutSession({
41
+ checkoutAttemptId: checkoutAttemptId ?? uuidv4(),
42
+ lines: lineItems.map((item) => ({
43
+ variantId: item.variantId,
44
+ quantity: item.quantity,
45
+ properties: item.properties.some((property) => {
46
+ return property.key === REPLO_ATTRIBUTION_PROPERTY.key;
47
+ }) ? item.properties : [REPLO_ATTRIBUTION_PROPERTY, ...item.properties]
48
+ })),
49
+ successUrl: `${origin}/checkout/success`,
50
+ cancelUrl,
51
+ uiMode: ui.mode
52
+ });
53
+ if (outcome.result !== "created") {
54
+ return outcome;
55
+ }
56
+ const session = outcome.session;
57
+ if (checkoutPage && session.form) {
58
+ const { hasLiveShipping, ...config } = session.form;
59
+ checkoutPage.hash = new URLSearchParams({
60
+ ...config,
61
+ ...hasLiveShipping ? { hasLiveShipping: "1" } : {}
62
+ }).toString();
63
+ return { result: "created", redirectUrl: checkoutPage.toString() };
64
+ }
65
+ if (!session.url) {
66
+ throw new StripeCheckoutError({
67
+ message: "Stripe checkout session response has no page to open"
68
+ });
69
+ }
70
+ return { result: "created", redirectUrl: session.url };
71
+ }
72
+ export {
73
+ StripeCheckoutError,
74
+ startStripeCheckout
75
+ };
@@ -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
+ };
@@ -1,6 +1,6 @@
1
1
  import { CONTENT_SEO_VALUE_KEYS, CONTENT_RELATED_VALUE_STORAGE_KEY, CONTENT_PRIVATE_VALUE_KEYS, LEGACY_CONTENT_ENTRY_STATUSES, isContentPrivateValueKey, getContentFieldValueSchema } from './chunk-RQ76VK3L.mjs';
2
- import { MapCache_default, getAllKeysIn_default, identity_default, isIndex_default, assignValue_default, keys_default, Stack_default, isLength_default, isArguments_default, getTag_default, isBuffer_default, isTypedArray_default, eq_default, Uint8Array_default, getAllKeys_default } from './chunk-XWWFRIXV.mjs';
3
- import { Symbol_default, isArray_default, isObject_default, isObjectLike_default, baseGetTag_default } from './chunk-ZPDXVFCR.mjs';
2
+ import { MapCache_default, getAllKeysIn_default, identity_default, isIndex_default, assignValue_default, keys_default, Stack_default, isLength_default, isArguments_default, getTag_default, isBuffer_default, isTypedArray_default, eq_default, Uint8Array_default, getAllKeys_default } from './chunk-N4WC2Z43.mjs';
3
+ import { Symbol_default, isArray_default, isObject_default, isObjectLike_default, baseGetTag_default } from './chunk-JBCOJHZN.mjs';
4
4
  import { stringify, parseDocument } from 'yaml';
5
5
  import * as z2 from 'zod';
6
6
 
@@ -1,5 +1,5 @@
1
- import { isString_default, isNumber_default } from './chunk-OPKMOW43.mjs';
2
- import { isObject_default } from './chunk-ZPDXVFCR.mjs';
1
+ import { isString_default, isNumber_default } from './chunk-GNIXARC7.mjs';
2
+ import { isObject_default } from './chunk-JBCOJHZN.mjs';
3
3
 
4
4
  var has = Object.prototype.hasOwnProperty;
5
5
  function dequal(foo, bar) {
@@ -1,4 +1,4 @@
1
- import { isObjectLike_default, baseGetTag_default, isArray_default } from './chunk-ZPDXVFCR.mjs';
1
+ import { isObjectLike_default, baseGetTag_default, isArray_default } from './chunk-JBCOJHZN.mjs';
2
2
 
3
3
  var numberTag = "[object Number]";
4
4
  function isNumber(value) {
@@ -1,4 +1,4 @@
1
- import { isObjectLike_default, root_default, freeGlobal_default, baseGetTag_default, isObject_default, isArray_default } from './chunk-ZPDXVFCR.mjs';
1
+ import { isObjectLike_default, root_default, freeGlobal_default, baseGetTag_default, isObject_default, isArray_default } from './chunk-JBCOJHZN.mjs';
2
2
 
3
3
  var asyncTag = "[object AsyncFunction]";
4
4
  var funcTag = "[object Function]";
@@ -1,4 +1,4 @@
1
- import { omit } from './chunk-O4QBR4QC.mjs';
1
+ import { omit } from './chunk-DMJ5VUD6.mjs';
2
2
 
3
3
  function parseJsonOrNull(json) {
4
4
  try {
package/env.d.ts CHANGED
@@ -3,6 +3,7 @@ type Env = {
3
3
  PROJECT_ID: string;
4
4
  ANALYTICS_FIRE_API_URL?: string;
5
5
  REPLO_ANALYTICS_PIXEL_URL?: string;
6
+ REPLO_SITE_VERSION?: string;
6
7
  CHECKOUT_PROVIDER?: "shopify" | "stripe" | "none";
7
8
  };
8
9
  export declare const getEnv: () => Promise<Env>;
package/env.js CHANGED
@@ -7,6 +7,7 @@ const envSchema = z.object({
7
7
  PROJECT_ID: z.uuid(),
8
8
  ANALYTICS_FIRE_API_URL: z.string().optional(),
9
9
  REPLO_ANALYTICS_PIXEL_URL: z.string().optional(),
10
+ REPLO_SITE_VERSION: z.string().optional(),
10
11
  CHECKOUT_PROVIDER: z.enum(["shopify", "stripe", "none"]).optional()
11
12
  });
12
13
  const wranglerConfigSchema = z.object({ vars: envSchema });
@@ -1,4 +1,5 @@
1
1
  import type { Cart } from "../cart/cart-schema";
2
+ import type { CartLineAttribute } from "../cart/cart-types";
2
3
  import type { StorefrontCountryCode } from "../loaders/markets";
3
4
  import * as z from "zod";
4
5
  import { CanopyError } from "../lib/canopy-error";
@@ -24,6 +25,68 @@ declare const reconciledCartLineSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
24
25
  status: z.ZodLiteral<"unavailable">;
25
26
  }, z.core.$strip>], "status">;
26
27
  export type ReconciledCartLine = z.infer<typeof reconciledCartLineSchema>;
28
+ declare const createdStripeSessionSchema: z.ZodObject<{
29
+ url: z.ZodNullable<z.ZodString>;
30
+ form: z.ZodOptional<z.ZodNullable<z.ZodObject<{
31
+ clientSecret: z.ZodString;
32
+ publishableKey: z.ZodString;
33
+ stripeAccountId: z.ZodString;
34
+ hasLiveShipping: z.ZodBoolean;
35
+ }, z.core.$strip>>>;
36
+ }, z.core.$strip>;
37
+ declare const outOfStockSchema: z.ZodObject<{
38
+ result: z.ZodLiteral<"outOfStock">;
39
+ variants: z.ZodArray<z.ZodObject<{
40
+ variantId: z.ZodString;
41
+ available: z.ZodNumber;
42
+ }, z.core.$strip>>;
43
+ }, z.core.$strip>;
44
+ declare const checkoutUpdateOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
45
+ type: z.ZodLiteral<"accept">;
46
+ }, z.core.$strip>, z.ZodObject<{
47
+ type: z.ZodLiteral<"reject">;
48
+ message: z.ZodString;
49
+ }, z.core.$strip>], "type">;
50
+ /**
51
+ * What the embedded checkout form shows after a cart or address change:
52
+ * `accept`, or `reject` with a message for the buyer.
53
+ */
54
+ export type CheckoutUpdateOutcome = z.infer<typeof checkoutUpdateOutcomeSchema>;
55
+ declare const crossSellSchema: z.ZodObject<{
56
+ productId: z.ZodString;
57
+ variantId: z.ZodString;
58
+ title: z.ZodString;
59
+ variantTitle: z.ZodNullable<z.ZodString>;
60
+ imageUrl: z.ZodNullable<z.ZodString>;
61
+ unitPriceMinor: z.ZodNumber;
62
+ currencyCode: z.ZodString;
63
+ }, z.core.$strip>;
64
+ /** A product offered beside the cart: one variant of a live product. */
65
+ export type CrossSell = z.infer<typeof crossSellSchema>;
66
+ declare const addCrossSellOutcomeSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
67
+ type: z.ZodLiteral<"accept">;
68
+ products: z.ZodArray<z.ZodObject<{
69
+ productId: z.ZodString;
70
+ variantId: z.ZodString;
71
+ title: z.ZodString;
72
+ variantTitle: z.ZodNullable<z.ZodString>;
73
+ imageUrl: z.ZodNullable<z.ZodString>;
74
+ unitPriceMinor: z.ZodNumber;
75
+ currencyCode: z.ZodString;
76
+ }, z.core.$strip>>;
77
+ }, z.core.$strip>, z.ZodObject<{
78
+ type: z.ZodLiteral<"reject">;
79
+ message: z.ZodString;
80
+ }, z.core.$strip>], "type">;
81
+ export type AddCrossSellOutcome = z.infer<typeof addCrossSellOutcomeSchema>;
82
+ export type CheckoutAddress = {
83
+ country: string;
84
+ line1: string;
85
+ line2: string | null;
86
+ city: string | null;
87
+ state: string | null;
88
+ postalCode: string | null;
89
+ };
27
90
  export declare function shouldIncludeInactiveProducts(): boolean;
28
91
  declare class CanopyApiGateway {
29
92
  private apiHost;
@@ -125,6 +188,40 @@ declare class CanopyApiGateway {
125
188
  value: string;
126
189
  }[];
127
190
  }): Promise<Cart>;
191
+ private _stripeCheckoutPost;
192
+ /**
193
+ * Opens a Stripe Checkout session for a cart. A hosted request's body is
194
+ * exactly what every published SDK sends, so canopy treats it the same.
195
+ */
196
+ createStripeCheckoutSession({ checkoutAttemptId, lines, successUrl, cancelUrl, uiMode, }: {
197
+ checkoutAttemptId: string;
198
+ lines: {
199
+ variantId: string;
200
+ quantity: number;
201
+ properties: CartLineAttribute[];
202
+ }[];
203
+ successUrl: string;
204
+ cancelUrl: string;
205
+ uiMode: "hosted" | "form";
206
+ }): Promise<{
207
+ result: "created";
208
+ session: z.infer<typeof createdStripeSessionSchema>;
209
+ } | {
210
+ result: "restartCheckout";
211
+ message: string;
212
+ } | z.infer<typeof outOfStockSchema>>;
213
+ updateCheckoutShipping(body: {
214
+ sessionId: string;
215
+ address: CheckoutAddress;
216
+ }): Promise<CheckoutUpdateOutcome>;
217
+ findCrossSells(body: {
218
+ sessionId: string | null;
219
+ excludeVariantIds: string[];
220
+ }): Promise<CrossSell[]>;
221
+ addCrossSell(body: {
222
+ sessionId: string;
223
+ variantId: string;
224
+ }): Promise<AddCrossSellOutcome>;
128
225
  }
129
226
  export declare function getCanopyApiGateway(): Promise<CanopyApiGateway>;
130
227
  export {};
@@ -49,6 +49,46 @@ const reconciledCartLineSchema = z.discriminatedUnion("status", [
49
49
  const reconcileCartLinesResponseSchema = z.object({
50
50
  lines: z.array(reconciledCartLineSchema)
51
51
  });
52
+ const createdStripeSessionSchema = z.object({
53
+ url: z.string().nullable(),
54
+ form: z.object({
55
+ clientSecret: z.string(),
56
+ publishableKey: z.string(),
57
+ stripeAccountId: z.string(),
58
+ hasLiveShipping: z.boolean()
59
+ }).nullish()
60
+ });
61
+ const outOfStockSchema = z.object({
62
+ result: z.literal("outOfStock"),
63
+ variants: z.array(
64
+ z.object({ variantId: z.string(), available: z.number().int().min(0) })
65
+ )
66
+ });
67
+ const restartCheckoutSchema = z.object({
68
+ error: z.literal("checkoutUnavailable"),
69
+ retry: z.literal("newAttempt"),
70
+ message: z.string().min(1)
71
+ });
72
+ const checkoutUpdateOutcomeSchema = z.discriminatedUnion("type", [
73
+ z.object({ type: z.literal("accept") }),
74
+ z.object({ type: z.literal("reject"), message: z.string() })
75
+ ]);
76
+ const crossSellSchema = z.object({
77
+ productId: z.string(),
78
+ variantId: z.string(),
79
+ title: z.string(),
80
+ variantTitle: z.string().nullable(),
81
+ imageUrl: z.string().nullable(),
82
+ unitPriceMinor: z.number(),
83
+ currencyCode: z.string()
84
+ });
85
+ const addCrossSellOutcomeSchema = z.discriminatedUnion("type", [
86
+ z.object({
87
+ type: z.literal("accept"),
88
+ products: z.array(crossSellSchema)
89
+ }),
90
+ z.object({ type: z.literal("reject"), message: z.string() })
91
+ ]);
52
92
  function shouldIncludeInactiveProducts() {
53
93
  return Boolean(process.env.IS_REPLO_SANDBOX);
54
94
  }
@@ -241,6 +281,86 @@ If you're running locally, make sure the canopy-api cloudflare worker is running
241
281
  }) {
242
282
  return this._cartPost("/replace-attributes", { cartId, attributes });
243
283
  }
284
+ async _stripeCheckoutPost({
285
+ path,
286
+ body,
287
+ schema
288
+ }) {
289
+ const env = await getEnv();
290
+ const response = await this._post(
291
+ `${this.baseUrl}/api/v1/stripe-checkout/${path}`,
292
+ { projectId: env.PROJECT_ID, ...body }
293
+ );
294
+ if (!response.ok) {
295
+ throw new CanopyApiGatewayError({
296
+ message: `Stripe checkout ${path} failed: ${response.status} ${response.statusText}`,
297
+ statusCode: response.status
298
+ });
299
+ }
300
+ return schema.parse(await response.json());
301
+ }
302
+ async createStripeCheckoutSession({
303
+ checkoutAttemptId,
304
+ lines,
305
+ successUrl,
306
+ cancelUrl,
307
+ uiMode
308
+ }) {
309
+ const env = await getEnv();
310
+ const response = await this._post(
311
+ `${this.baseUrl}/api/v1/stripe-checkout/create-session`,
312
+ {
313
+ cartId: checkoutAttemptId,
314
+ checkoutAttemptId,
315
+ lines,
316
+ projectId: env.PROJECT_ID,
317
+ successUrl,
318
+ cancelUrl,
319
+ ...uiMode === "form" ? { uiMode } : {}
320
+ }
321
+ );
322
+ if (!response.ok) {
323
+ const restart = response.status === 409 ? restartCheckoutSchema.safeParse(await response.json()) : null;
324
+ if (restart?.success) {
325
+ return { result: "restartCheckout", message: restart.data.message };
326
+ }
327
+ throw new CanopyApiGatewayError({
328
+ message: `Failed to create Stripe checkout session: ${response.status} ${response.statusText}`,
329
+ statusCode: response.status
330
+ });
331
+ }
332
+ const payload = await response.json();
333
+ const outOfStock = outOfStockSchema.safeParse(payload);
334
+ if (outOfStock.success) {
335
+ return outOfStock.data;
336
+ }
337
+ return {
338
+ result: "created",
339
+ session: createdStripeSessionSchema.parse(payload)
340
+ };
341
+ }
342
+ async updateCheckoutShipping(body) {
343
+ return await this._stripeCheckoutPost({
344
+ path: "update-shipping",
345
+ body,
346
+ schema: checkoutUpdateOutcomeSchema
347
+ });
348
+ }
349
+ async findCrossSells(body) {
350
+ const { products } = await this._stripeCheckoutPost({
351
+ path: "cross-sells",
352
+ body,
353
+ schema: z.object({ products: z.array(crossSellSchema) })
354
+ });
355
+ return products;
356
+ }
357
+ async addCrossSell(body) {
358
+ return await this._stripeCheckoutPost({
359
+ path: "add-cross-sell",
360
+ body,
361
+ schema: addCrossSellOutcomeSchema
362
+ });
363
+ }
244
364
  }
245
365
  async function getCanopyApiGateway() {
246
366
  return CanopyApiGateway.setup();
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sdk",
3
- "version": "2.4.0",
3
+ "version": "2.7.0",
4
4
  "branch": "HEAD",
5
- "commit": "a20152029da76f3b8edd0d538187209442f20e23",
6
- "builtAt": "2026-10-22T05:49:26.854Z"
5
+ "commit": "1aa1f09e1dd141b243b7bb3b1ff1bc947fa01371",
6
+ "builtAt": "2026-10-23T05:44:46.035Z"
7
7
  };
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@replohq/sdk",
3
- "version": "2.4.0",
3
+ "version": "2.7.0",
4
4
  "reploBuild": {
5
5
  "packageName": "@replohq/sdk",
6
- "version": "2.4.0",
6
+ "version": "2.7.0",
7
7
  "branch": "HEAD",
8
- "commit": "a20152029da76f3b8edd0d538187209442f20e23",
9
- "builtAt": "2026-10-22T05:49:26.854Z"
8
+ "commit": "1aa1f09e1dd141b243b7bb3b1ff1bc947fa01371",
9
+ "builtAt": "2026-10-23T05:44:46.035Z"
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",
@@ -49,6 +49,14 @@
49
49
  "types": "./cart/buy-now-action.d.ts",
50
50
  "default": "./cart/buy-now-action.js"
51
51
  },
52
+ "./cart/checkout": {
53
+ "types": "./cart/checkout.d.ts",
54
+ "default": "./cart/checkout.js"
55
+ },
56
+ "./cart/checkout-actions": {
57
+ "types": "./cart/checkout-actions.d.ts",
58
+ "default": "./cart/checkout-actions.js"
59
+ },
52
60
  "./cart/utils/cart-utils": {
53
61
  "types": "./cart/utils/cart-utils.d.ts",
54
62
  "default": "./cart/utils/cart-utils.js"
@@ -310,6 +318,8 @@
310
318
  "dependencies": {
311
319
  "@formatjs/intl-localematcher": "^0.8.13",
312
320
  "@kachingappz/bundles-react": "0.5.3",
321
+ "@stripe/react-stripe-js": "7.0.0",
322
+ "@stripe/stripe-js": "10.1.0",
313
323
  "negotiator": "^1.0.0",
314
324
  "react-markdown": "^9.1.0",
315
325
  "remark-gfm": "^4.0.1",
@@ -1,84 +0,0 @@
1
- type ValueOfType<ObjectType> = ObjectType[keyof ObjectType];
2
- /**
3
- * Mostly taken from
4
- * https://gist.githubusercontent.com/scokmen/f813c904ef79022e84ab2409574d1b45/raw/cd8709a2fccb005bb53e9bfb2461e07d40b4e8d8/HttpStatusCode.ts
5
- */
6
- export declare const HTTP_STATUS_CODES: {
7
- readonly CONTINUE: 100;
8
- readonly SWITCHING_PROTOCOLS: 101;
9
- readonly PROCESSING: 102;
10
- readonly OK: 200;
11
- readonly CREATED: 201;
12
- readonly ACCEPTED: 202;
13
- readonly NON_AUTHORITATIVE_INFORMATION: 203;
14
- readonly NO_CONTENT: 204;
15
- readonly RESET_CONTENT: 205;
16
- readonly PARTIAL_CONTENT: 206;
17
- readonly MULTI_STATUS: 207;
18
- readonly ALREADY_REPORTED: 208;
19
- readonly IM_USED: 226;
20
- readonly MULTIPLE_CHOICES: 300;
21
- readonly MOVED_PERMANENTLY: 301;
22
- readonly FOUND: 302;
23
- readonly SEE_OTHER: 303;
24
- readonly NOT_MODIFIED: 304;
25
- readonly USE_PROXY: 305;
26
- readonly SWITCH_PROXY: 306;
27
- readonly TEMPORARY_REDIRECT: 307;
28
- readonly PERMANENT_REDIRECT: 308;
29
- readonly BAD_REQUEST: 400;
30
- readonly UNAUTHORIZED: 401;
31
- readonly PAYMENT_REQUIRED: 402;
32
- readonly FORBIDDEN: 403;
33
- readonly NOT_FOUND: 404;
34
- readonly METHOD_NOT_ALLOWED: 405;
35
- readonly NOT_ACCEPTABLE: 406;
36
- readonly PROXY_AUTHENTICATION_REQUIRED: 407;
37
- readonly REQUEST_TIMEOUT: 408;
38
- readonly CONFLICT: 409;
39
- readonly GONE: 410;
40
- readonly LENGTH_REQUIRED: 411;
41
- readonly PRECONDITION_FAILED: 412;
42
- readonly PAYLOAD_TOO_LARGE: 413;
43
- readonly URI_TOO_LONG: 414;
44
- readonly UNSUPPORTED_MEDIA_TYPE: 415;
45
- readonly RANGE_NOT_SATISFIABLE: 416;
46
- readonly EXPECTATION_FAILED: 417;
47
- readonly I_AM_A_TEAPOT: 418;
48
- readonly MISDIRECTED_REQUEST: 421;
49
- readonly UNPROCESSABLE_ENTITY: 422;
50
- readonly LOCKED: 423;
51
- readonly FAILED_DEPENDENCY: 424;
52
- readonly UPGRADE_REQUIRED: 426;
53
- readonly PRECONDITION_REQUIRED: 428;
54
- readonly TOO_MANY_REQUESTS: 429;
55
- readonly REQUEST_HEADER_FIELDS_TOO_LARGE: 431;
56
- readonly UNAVAILABLE_FOR_LEGAL_REASONS: 451;
57
- readonly INTERNAL_SERVER_ERROR: 500;
58
- readonly NOT_IMPLEMENTED: 501;
59
- readonly BAD_GATEWAY: 502;
60
- readonly SERVICE_UNAVAILABLE: 503;
61
- readonly GATEWAY_TIMEOUT: 504;
62
- readonly HTTP_VERSION_NOT_SUPPORTED: 505;
63
- readonly VARIANT_ALSO_NEGOTIATES: 506;
64
- readonly INSUFFICIENT_STORAGE: 507;
65
- readonly LOOP_DETECTED: 508;
66
- readonly NOT_EXTENDED: 510;
67
- readonly NETWORK_AUTHENTICATION_REQUIRED: 511;
68
- readonly UPSTREAM_WEBSERVER_ERROR: 520;
69
- };
70
- export type HttpStatusCode = ValueOfType<typeof HTTP_STATUS_CODES>;
71
- /**
72
- * Checks if an error message indicates a network error (e.g., connectivity issues,
73
- * request timeouts, DNS failures). These errors typically don't need to be tracked
74
- * in error monitoring systems like Sentry since they're user-side connectivity issues.
75
- */
76
- export declare function isNetworkErrorMessage(message: string): boolean;
77
- /**
78
- * Extracts a human-readable message from an unknown caught value. If the value
79
- * is an Error, returns its message; otherwise falls back to the provided
80
- * fallback string or `String(error)`.
81
- */
82
- export declare function getErrorMessage(error: unknown, fallback?: string): string;
83
- export declare function appendErrorId(detail: string, errorId: string | null | undefined): string;
84
- export {};