@replohq/sdk 1.4.1 → 1.6.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/url.d.ts +5 -1
  2. package/_vendor/schemas/loaderKeys.mjs +2 -0
  3. package/cart/cart-actions.js +2 -1
  4. package/cart/cart-actions.js.map +2 -2
  5. package/cart/cart-provider.d.ts +15 -3
  6. package/cart/cart-provider.js +49 -6
  7. package/cart/cart-provider.js.map +2 -2
  8. package/cart/cart-types.d.ts +15 -0
  9. package/cart/cart-types.js.map +1 -1
  10. package/cart/gateways/cart-gateway.d.ts +7 -0
  11. package/cart/gateways/cart-gateway.js.map +2 -2
  12. package/cart/gateways/cookie-cart-gateway.d.ts +9 -0
  13. package/cart/gateways/cookie-cart-gateway.js +54 -3
  14. package/cart/gateways/cookie-cart-gateway.js.map +2 -2
  15. package/cart/gateways/resolver-cart-gateway.js +4 -0
  16. package/cart/gateways/resolver-cart-gateway.js.map +2 -2
  17. package/cart/gateways/stripe-cart-gateway.d.ts +14 -0
  18. package/cart/gateways/stripe-cart-gateway.js +6 -1
  19. package/cart/gateways/stripe-cart-gateway.js.map +2 -2
  20. package/cart/hooks/use-add-to-cart.d.ts +3 -1
  21. package/cart/hooks/use-add-to-cart.js +1 -1
  22. package/cart/hooks/use-add-to-cart.js.map +2 -2
  23. package/cart/utils/cart-adjustments.d.ts +29 -0
  24. package/cart/utils/cart-adjustments.js +81 -0
  25. package/cart/utils/cart-adjustments.js.map +7 -0
  26. package/cart/utils/cart-utils.d.ts +14 -0
  27. package/cart/utils/cart-utils.js +43 -0
  28. package/cart/utils/cart-utils.js.map +2 -2
  29. package/cart/utils/cookie-cart-persistence.d.ts +4 -2
  30. package/cart/utils/cookie-cart-persistence.js +35 -21
  31. package/cart/utils/cookie-cart-persistence.js.map +3 -3
  32. package/gateways/canopy-api.d.ts +25 -0
  33. package/gateways/canopy-api.js +40 -0
  34. package/gateways/canopy-api.js.map +2 -2
  35. package/lib/buildMetadata.js +3 -3
  36. package/loaders/judgeme-review-aggregate-loader.d.ts +12 -0
  37. package/loaders/judgeme-review-aggregate-loader.js +50 -0
  38. package/loaders/judgeme-review-aggregate-loader.js.map +7 -0
  39. package/loaders/judgeme-reviews-loader.d.ts +14 -0
  40. package/loaders/judgeme-reviews-loader.js +65 -0
  41. package/loaders/judgeme-reviews-loader.js.map +7 -0
  42. package/loaders/loader-keys.d.ts +2 -0
  43. package/loaders/loader-keys.js +2 -0
  44. package/loaders/loader-keys.js.map +2 -2
  45. package/loaders/loader-schemas.d.ts +30 -0
  46. package/loaders/loader-schemas.js +22 -0
  47. package/loaders/loader-schemas.js.map +2 -2
  48. package/loaders/rebuy-recommendations-loader.d.ts +1 -0
  49. package/next/config.d.ts +18 -0
  50. package/next/config.js.map +2 -2
  51. package/package.json +12 -4
  52. package/providers/replo-provider.d.ts +19 -0
  53. package/providers/replo-provider.js.map +2 -2
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../cart/utils/cookie-cart-persistence.ts"],
4
- "sourcesContent": ["\"use server\";\n\nimport type {\n AddToCartPayload,\n Cart,\n CartLine,\n CartLinePayload,\n RemoveFromCartPayload,\n UpdateCartLineItemPayload,\n} from \"../cart-types\";\n\nimport { cookies } from \"next/headers\";\n\nimport { getCartCookieOptions } from \"../gateways/cart-constants\";\nimport { recalculateCartCost } from \"./cart-utils\";\n\n/**\n * Line merge function type - returns index of existing line to merge with, or -1 to add as new line\n */\nexport type LineMergeFn = (\n existingLines: CartLine[],\n newLine: CartLinePayload,\n) => number;\n\n/**\n * Options for creating a cart with persistence\n */\ninterface CreateCartOptions {\n cookieName: string;\n lines?: CartLinePayload[];\n lineIdGenerator: () => string;\n cartIdGenerator: () => string;\n}\n\n/**\n * Options for adding to cart with persistence\n */\ninterface AddToCartOptions {\n cookieName: string;\n mergeFn: LineMergeFn;\n lineIdGenerator: () => string;\n}\n\n/**\n * Save cart to cookie\n */\nexport async function saveCartToCookie(cart: Cart, cookieName: string) {\n const cookieStore = await cookies();\n cookieStore.set(cookieName, JSON.stringify(cart), getCartCookieOptions());\n}\n\n/**\n * Load cart from cookie\n */\nexport async function loadCartFromCookie(\n cookieName: string,\n): Promise<Cart | null> {\n const cookieStore = await cookies();\n const cartCookie = cookieStore.get(cookieName);\n\n if (!cartCookie?.value) {\n return null;\n }\n\n try {\n // Legacy cast - may or may not be necessary\n // eslint-disable-next-line replo/no-as-cast\n return JSON.parse(cartCookie.value) as Cart;\n } catch (error) {\n console.warn(\"Failed to parse cart cookie:\", error);\n await clearCartCookie(cookieName);\n return null;\n }\n}\n\n/**\n * Clear cart cookie\n */\nexport async function clearCartCookie(cookieName: string) {\n const cookieStore = await cookies();\n // NOTE (Max, 2026-08-25): `delete` omits Partitioned, which leaves the\n // partitioned cookie in place \u2014 expire it with the attributes it was set with.\n cookieStore.set(cookieName, \"\", { ...getCartCookieOptions(), maxAge: 0 });\n}\n\n/**\n * Update cart in cookie atomically (load-transform-save pattern)\n */\nexport async function updateCartInCookie(\n cartId: string,\n cookieName: string,\n updater: (cart: Cart) => Cart,\n): Promise<Cart | null> {\n const existingCart = await loadCartFromCookie(cookieName);\n\n if (!existingCart || existingCart.id !== cartId) {\n return null;\n }\n\n const updatedCart = recalculateCartCost(updater(existingCart));\n\n await saveCartToCookie(updatedCart, cookieName);\n\n return updatedCart;\n}\n\n/**\n * Create a new cart with persistence\n */\nexport async function createCartWithPersistence(\n options: CreateCartOptions,\n): Promise<Cart | null> {\n const cartId = options.cartIdGenerator();\n\n const cart: Cart = {\n id: cartId,\n lines: [],\n cost: {\n subtotalAmount: 0,\n totalAmount: 0,\n currencyCode: \"USD\",\n },\n checkoutUrl: \"/checkout\",\n };\n\n for (const line of options.lines ?? []) {\n cart.lines.push(\n convertCartLinePayloadToCartLine(line, options.lineIdGenerator),\n );\n }\n\n const cartWithCost = recalculateCartCost(cart);\n await saveCartToCookie(cartWithCost, options.cookieName);\n return cartWithCost;\n}\n\n/**\n * Add lines to cart with configurable line merging strategy\n */\nexport async function addToCartWithPersistence(\n payload: AddToCartPayload,\n options: AddToCartOptions,\n): Promise<Cart | null> {\n return await updateCartInCookie(\n payload.cartId,\n options.cookieName,\n (cart) => {\n for (const linePayload of payload.lines) {\n const existingLineIndex = options.mergeFn(cart.lines, linePayload);\n\n if (existingLineIndex >= 0) {\n cart.lines[existingLineIndex]!.quantity += linePayload.quantity;\n } else {\n const newLine: CartLine = convertCartLinePayloadToCartLine(\n linePayload,\n options.lineIdGenerator,\n );\n cart.lines.push(newLine);\n }\n }\n\n return cart;\n },\n );\n}\n\n/**\n * Update cart line item\n */\nexport async function updateCartLineItemWithPersistence(\n payload: UpdateCartLineItemPayload,\n cookieName: string,\n): Promise<Cart | null> {\n return await updateCartInCookie(payload.cartId, cookieName, (cart) => {\n const lineIndex = cart.lines.findIndex(\n (line) => line.id === payload.line.id,\n );\n\n if (lineIndex >= 0) {\n if (payload.line.quantity <= 0) {\n cart.lines.splice(lineIndex, 1);\n } else {\n cart.lines[lineIndex]!.quantity = payload.line.quantity;\n if (payload.line.properties) {\n cart.lines[lineIndex]!.attributes = payload.line.properties;\n }\n if (payload.line.merchandise) {\n cart.lines[lineIndex]!.merchandise = payload.line.merchandise;\n }\n }\n }\n\n return cart;\n });\n}\n\n/**\n * Remove cart line items\n */\nexport async function removeCartLineItemsWithPersistence(\n payload: RemoveFromCartPayload,\n cookieName: string,\n): Promise<Cart | null> {\n return await updateCartInCookie(payload.cartId, cookieName, (cart) => {\n const removeSet = new Set(payload.lineIds);\n cart.lines = cart.lines.filter((line) => !removeSet.has(line.id));\n return cart;\n });\n}\n\n/**\n * Convert CartLinePayload to CartLine with custom ID generator\n */\nfunction convertCartLinePayloadToCartLine(\n linePayload: CartLinePayload,\n lineIdGenerator: () => string,\n): CartLine {\n // Cookie-persisted carts (Stripe/default) are their own source of truth \u2014\n // there's no server roundtrip to backfill merchandise later. Fall back to a\n // placeholder when the caller didn't pass merchandise so the persisted line\n // is renderable; otherwise an unresolvable line would render as a skeleton\n // forever. Shopify carts hit a different code path and use the skeleton\n // treatment in cart-provider.tsx.\n const fallbackMerchandise = {\n id: linePayload.id,\n product: {\n id: linePayload.merchandise?.product.id ?? linePayload.id,\n title: linePayload.merchandise?.product.title ?? \"Product\",\n },\n title: linePayload.merchandise?.title ?? \"Product Variant\",\n price: linePayload.merchandise?.price ?? 0,\n compareAtPrice: linePayload.merchandise?.compareAtPrice ?? null,\n image: linePayload.merchandise?.image\n ? {\n url: linePayload.merchandise.image.url,\n altText: linePayload.merchandise.image.altText,\n }\n : null,\n selectedOptions: linePayload.merchandise?.selectedOptions ?? [],\n };\n\n return {\n id: lineIdGenerator(),\n quantity: linePayload.quantity,\n merchandise: linePayload.merchandise ?? fallbackMerchandise,\n attributes: linePayload.properties ?? [],\n sellingPlanAllocation: linePayload.sellingPlanId\n ? {\n sellingPlan: {\n id: linePayload.sellingPlanId,\n name: \"Subscription Plan\",\n description: null,\n },\n priceAdjustments: [],\n checkoutChargeAmount: { amount: 0, currencyCode: \"USD\" },\n remainingBalanceChargeAmount: { amount: 0, currencyCode: \"USD\" },\n }\n : null,\n };\n}\n"],
5
- "mappings": "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",
6
- "names": []
4
+ "sourcesContent": ["\"use server\";\n\nimport type {\n AddToCartPayload,\n Cart,\n CartLine,\n CartLinePayload,\n RemoveFromCartPayload,\n UpdateCartLineItemPayload,\n} from \"../cart-types\";\n\nimport { cookies } from \"next/headers\";\n\nimport { getCartCookieOptions } from \"../gateways/cart-constants\";\nimport { recalculateCartCost } from \"./cart-utils\";\n\n/**\n * Line merge function type - returns index of existing line to merge with, or -1 to add as new line\n */\nexport type LineMergeFn = (\n existingLines: CartLine[],\n newLine: CartLinePayload,\n) => number;\n\n/**\n * Options for creating a cart with persistence\n */\ninterface CreateCartOptions {\n cookieName: string;\n lines?: CartLinePayload[];\n lineIdGenerator: () => string;\n cartIdGenerator: () => string;\n reconcile?: (cart: Cart) => Promise<Cart>;\n}\n\n/**\n * Options for adding to cart with persistence\n */\ninterface AddToCartOptions {\n cookieName: string;\n mergeFn: LineMergeFn;\n lineIdGenerator: () => string;\n reconcile?: (cart: Cart) => Promise<Cart>;\n}\n\n/**\n * Save cart to cookie\n */\nexport async function saveCartToCookie(cart: Cart, cookieName: string) {\n const cookieStore = await cookies();\n cookieStore.set(cookieName, JSON.stringify(cart), getCartCookieOptions());\n}\n\n/**\n * Load cart from cookie\n */\nexport async function loadCartFromCookie(\n cookieName: string,\n): Promise<Cart | null> {\n const cookieStore = await cookies();\n const cartCookie = cookieStore.get(cookieName);\n\n if (!cartCookie?.value) {\n return null;\n }\n\n try {\n // Legacy cast - may or may not be necessary\n // eslint-disable-next-line replo/no-as-cast\n return JSON.parse(cartCookie.value) as Cart;\n } catch (error) {\n console.warn(\"Failed to parse cart cookie:\", error);\n await clearCartCookie(cookieName);\n return null;\n }\n}\n\n/**\n * Clear cart cookie\n */\nexport async function clearCartCookie(cookieName: string) {\n const cookieStore = await cookies();\n // NOTE (Max, 2026-08-25): `delete` omits Partitioned, which leaves the\n // partitioned cookie in place \u2014 expire it with the attributes it was set with.\n cookieStore.set(cookieName, \"\", { ...getCartCookieOptions(), maxAge: 0 });\n}\n\n/**\n * Update cart in cookie atomically (load-transform-save pattern)\n */\nexport async function updateCartInCookie(\n cartId: string,\n cookieName: string,\n updater: (cart: Cart) => Cart,\n reconcile?: (cart: Cart) => Promise<Cart>,\n): Promise<Cart | null> {\n const existingCart = await loadCartFromCookie(cookieName);\n\n if (!existingCart || existingCart.id !== cartId) {\n return null;\n }\n\n let updatedCart = recalculateCartCost(updater(existingCart));\n if (reconcile) {\n updatedCart = await reconcile(updatedCart);\n }\n\n await saveCartToCookie(updatedCart, cookieName);\n\n return updatedCart;\n}\n\n/**\n * Create a new cart with persistence\n */\nexport async function createCartWithPersistence(\n options: CreateCartOptions,\n): Promise<Cart | null> {\n const cartId = options.cartIdGenerator();\n\n const cart: Cart = {\n id: cartId,\n lines: [],\n cost: {\n subtotalAmount: 0,\n totalAmount: 0,\n currencyCode: \"USD\",\n },\n checkoutUrl: \"/checkout\",\n };\n\n for (const line of options.lines ?? []) {\n cart.lines.push(\n convertCartLinePayloadToCartLine(line, options.lineIdGenerator),\n );\n }\n\n let cartWithCost = recalculateCartCost(cart);\n if (options.reconcile) {\n cartWithCost = await options.reconcile(cartWithCost);\n }\n await saveCartToCookie(cartWithCost, options.cookieName);\n return cartWithCost;\n}\n\n/**\n * Add lines to cart with configurable line merging strategy\n */\nexport async function addToCartWithPersistence(\n payload: AddToCartPayload,\n options: AddToCartOptions,\n): Promise<Cart | null> {\n return await updateCartInCookie(\n payload.cartId,\n options.cookieName,\n (cart) => {\n for (const linePayload of payload.lines) {\n const existingLineIndex = options.mergeFn(cart.lines, linePayload);\n\n if (existingLineIndex >= 0) {\n cart.lines[existingLineIndex]!.quantity += linePayload.quantity;\n } else {\n const newLine: CartLine = convertCartLinePayloadToCartLine(\n linePayload,\n options.lineIdGenerator,\n );\n cart.lines.push(newLine);\n }\n }\n\n return cart;\n },\n options.reconcile,\n );\n}\n\n/**\n * Update cart line item\n */\nexport async function updateCartLineItemWithPersistence(\n payload: UpdateCartLineItemPayload,\n cookieName: string,\n reconcile?: (cart: Cart) => Promise<Cart>,\n): Promise<Cart | null> {\n return await updateCartInCookie(\n payload.cartId,\n cookieName,\n (cart) => {\n const lineIndex = cart.lines.findIndex(\n (line) => line.id === payload.line.id,\n );\n if (lineIndex < 0) {\n return cart;\n }\n if (payload.line.quantity <= 0) {\n cart.lines.splice(lineIndex, 1);\n return cart;\n }\n const line = cart.lines[lineIndex]!;\n line.quantity = payload.line.quantity;\n if (payload.line.properties) {\n line.attributes = payload.line.properties;\n }\n if (payload.line.merchandise) {\n line.merchandise = payload.line.merchandise;\n }\n return cart;\n },\n reconcile,\n );\n}\n\n/**\n * Remove cart line items\n */\nexport async function removeCartLineItemsWithPersistence(\n payload: RemoveFromCartPayload,\n cookieName: string,\n): Promise<Cart | null> {\n return await updateCartInCookie(payload.cartId, cookieName, (cart) => {\n const removeSet = new Set(payload.lineIds);\n cart.lines = cart.lines.filter((line) => !removeSet.has(line.id));\n return cart;\n });\n}\n\n/**\n * Convert CartLinePayload to CartLine with custom ID generator\n */\nfunction convertCartLinePayloadToCartLine(\n linePayload: CartLinePayload,\n lineIdGenerator: () => string,\n): CartLine {\n // Cookie-persisted carts (Stripe/default) are their own source of truth \u2014\n // there's no server roundtrip to backfill merchandise later. Fall back to a\n // placeholder when the caller didn't pass merchandise so the persisted line\n // is renderable; otherwise an unresolvable line would render as a skeleton\n // forever. Shopify carts hit a different code path and use the skeleton\n // treatment in cart-provider.tsx.\n const fallbackMerchandise = {\n id: linePayload.id,\n product: {\n id: linePayload.merchandise?.product.id ?? linePayload.id,\n title: linePayload.merchandise?.product.title ?? \"Product\",\n },\n title: linePayload.merchandise?.title ?? \"Product Variant\",\n price: linePayload.merchandise?.price ?? 0,\n compareAtPrice: linePayload.merchandise?.compareAtPrice ?? null,\n image: linePayload.merchandise?.image\n ? {\n url: linePayload.merchandise.image.url,\n altText: linePayload.merchandise.image.altText,\n }\n : null,\n selectedOptions: linePayload.merchandise?.selectedOptions ?? [],\n };\n\n return {\n id: lineIdGenerator(),\n quantity: linePayload.quantity,\n merchandise: linePayload.merchandise ?? fallbackMerchandise,\n attributes: linePayload.properties ?? [],\n sellingPlanAllocation: linePayload.sellingPlanId\n ? {\n sellingPlan: {\n id: linePayload.sellingPlanId,\n name: \"Subscription Plan\",\n description: null,\n },\n priceAdjustments: [],\n checkoutChargeAmount: { amount: 0, currencyCode: \"USD\" },\n remainingBalanceChargeAmount: { amount: 0, currencyCode: \"USD\" },\n }\n : null,\n };\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["line"]
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import type { Cart } from "../cart/cart-schema";
2
+ import { z } from "zod";
2
3
  import { CanopyError } from "../lib/canopy-error";
3
4
  type CanopyApiGatewayErrorConfig = {
4
5
  message: string;
@@ -10,6 +11,18 @@ export declare class CanopyApiGatewayError extends CanopyError {
10
11
  statusCode: number;
11
12
  constructor(config: CanopyApiGatewayErrorConfig);
12
13
  }
14
+ declare const reconciledCartLineSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
15
+ variantId: z.ZodString;
16
+ status: z.ZodLiteral<"purchasable">;
17
+ unitPriceMinor: z.ZodNumber;
18
+ compareAtPriceMinor: z.ZodNullable<z.ZodNumber>;
19
+ currencyCode: z.ZodString;
20
+ purchasableQuantity: z.ZodNumber;
21
+ }, z.core.$strip>, z.ZodObject<{
22
+ variantId: z.ZodString;
23
+ status: z.ZodLiteral<"unavailable">;
24
+ }, z.core.$strip>], "status">;
25
+ export type ReconciledCartLine = z.infer<typeof reconciledCartLineSchema>;
13
26
  declare class CanopyApiGateway {
14
27
  private apiHost;
15
28
  private baseUrl;
@@ -30,6 +43,18 @@ declare class CanopyApiGateway {
30
43
  };
31
44
  } | undefined;
32
45
  }>;
46
+ /**
47
+ * The catalog's current answer for a set of cookie-cart lines: per line,
48
+ * the live price and how many units are actually purchasable. Used to
49
+ * reconcile the client-persisted cart, which otherwise never re-reads the
50
+ * catalog facts it snapshotted at add-to-cart time.
51
+ */
52
+ reconcileCartLines({ lines, }: {
53
+ lines: {
54
+ variantId: string;
55
+ quantity: number;
56
+ }[];
57
+ }): Promise<ReconciledCartLine[]>;
33
58
  private _cartPost;
34
59
  cartGet({ cartId }: {
35
60
  cartId: string;
@@ -32,6 +32,23 @@ const loaderInvokeResponseSchema = z.object({
32
32
  })
33
33
  }).optional()
34
34
  });
35
+ const reconciledCartLineSchema = z.discriminatedUnion("status", [
36
+ z.object({
37
+ variantId: z.string(),
38
+ status: z.literal("purchasable"),
39
+ unitPriceMinor: z.number().int(),
40
+ compareAtPriceMinor: z.number().int().nullable(),
41
+ currencyCode: z.string(),
42
+ purchasableQuantity: z.number().int().min(0)
43
+ }),
44
+ z.object({
45
+ variantId: z.string(),
46
+ status: z.literal("unavailable")
47
+ })
48
+ ]);
49
+ const reconcileCartLinesResponseSchema = z.object({
50
+ lines: z.array(reconciledCartLineSchema)
51
+ });
35
52
  class CanopyApiGateway {
36
53
  apiHost;
37
54
  baseUrl;
@@ -112,6 +129,29 @@ If you're running locally, make sure the canopy-api cloudflare worker is running
112
129
  }
113
130
  return loaderInvokeResponseSchema.parse(await response.json());
114
131
  }
132
+ /**
133
+ * The catalog's current answer for a set of cookie-cart lines: per line,
134
+ * the live price and how many units are actually purchasable. Used to
135
+ * reconcile the client-persisted cart, which otherwise never re-reads the
136
+ * catalog facts it snapshotted at add-to-cart time.
137
+ */
138
+ async reconcileCartLines({
139
+ lines
140
+ }) {
141
+ const env = await getEnv();
142
+ const response = await this._post(
143
+ `${this.baseUrl}/api/v1/product-catalog/reconcile-cart-lines`,
144
+ { projectId: env.PROJECT_ID, lines }
145
+ );
146
+ if (!response.ok) {
147
+ const errorBody = await response.text().catch(() => "");
148
+ throw new CanopyApiGatewayError({
149
+ message: `Failed to reconcile cart lines: ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : ""}`,
150
+ statusCode: response.status
151
+ });
152
+ }
153
+ return reconcileCartLinesResponseSchema.parse(await response.json()).lines;
154
+ }
115
155
  // ---------------------------------------------------------------------------
116
156
  // Cart operations (canopy-api-backed ExternalCart)
117
157
  // ---------------------------------------------------------------------------
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../gateways/canopy-api.ts"],
4
- "sourcesContent": ["import type { Cart } from \"../cart/cart-schema\";\n\nimport { z } from \"zod\";\n\nimport { cartSchema } from \"../cart/cart-schema\";\nimport { getEnv } from \"../env\";\nimport { CanopyError } from \"../lib/canopy-error\";\nimport { buildBaseUrl } from \"../lib/url-utils\";\n\ntype CanopyApiGatewayErrorConfig = {\n message: string;\n statusCode?: number;\n cause?: unknown;\n additionalData?: Record<string, unknown>;\n};\n\nexport class CanopyApiGatewayError extends CanopyError {\n statusCode: number;\n\n constructor(config: CanopyApiGatewayErrorConfig) {\n const resolvedStatusCode = config.statusCode ?? 500;\n super({\n message: config.message,\n cause: config.cause,\n additionalData: {\n ...config.additionalData,\n statusCode: resolvedStatusCode,\n },\n });\n this.statusCode = resolvedStatusCode;\n }\n}\n\nclass UnknownCanopyApiError extends CanopyApiGatewayError {\n constructor(config: Omit<CanopyApiGatewayErrorConfig, \"statusCode\">) {\n super({ ...config, statusCode: 502 });\n }\n}\n\nconst loaderInvokeResponseSchema = z.object({\n data: z.unknown(),\n meta: z\n .object({\n cache: z.object({\n hit: z.boolean(),\n ttlSeconds: z.number(),\n }),\n })\n .optional(),\n});\n\nclass CanopyApiGateway {\n private apiHost: string;\n private baseUrl: string;\n constructor(apiHost: string) {\n this.apiHost = apiHost;\n this.baseUrl = buildBaseUrl(apiHost);\n }\n\n private static readonly REQUEST_TIMEOUT_MILLISECONDS = 10_000;\n\n static async setup() {\n const env = await getEnv();\n\n return new CanopyApiGateway(env.CANOPY_API_HOST);\n }\n\n private async _post(url: string, body: unknown) {\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n try {\n return await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: abortController.signal,\n });\n } catch (error) {\n let message = `Failed to POST to canopy-api at ${url}: ${error}`;\n if (process.env.NODE_ENV !== \"production\") {\n message += `\\n\\napiHost: ${this.apiHost}\\nIf you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;\n }\n throw new UnknownCanopyApiError({ message, cause: error });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private async _get(url: string) {\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n try {\n return await fetch(url, { signal: abortController.signal });\n } catch (error) {\n let message = `Failed to GET from canopy-api at ${url}: ${error}`;\n if (process.env.NODE_ENV !== \"production\") {\n message += `\\n\\napiHost: ${this.apiHost}\\nIf you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;\n }\n throw new UnknownCanopyApiError({ message, cause: error });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async invokeLoader({\n loaderKey,\n args,\n }: {\n loaderKey: string;\n args: Record<string, unknown>;\n }) {\n const env = await getEnv();\n const response = await this._post(`${this.baseUrl}/api/v1/loaders/invoke`, {\n projectId: env.PROJECT_ID,\n loaderKey,\n args,\n // The builder preview must render a product the merchant hasn't launched\n // yet; a published storefront must not. Only the sandbox dev server sets\n // IS_REPLO_SANDBOX, so it is what separates the two.\n includeInactiveProducts: Boolean(process.env.IS_REPLO_SANDBOX),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n throw new CanopyApiGatewayError({\n message: `Failed to invoke loader ${loaderKey}: ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : \"\"}`,\n statusCode: response.status,\n });\n }\n\n return loaderInvokeResponseSchema.parse(await response.json());\n }\n\n // ---------------------------------------------------------------------------\n // Cart operations (canopy-api-backed ExternalCart)\n // ---------------------------------------------------------------------------\n\n private async _cartPost(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Cart> {\n const env = await getEnv();\n const url = `${this.baseUrl}/api/v1/cart${path}`;\n\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ ...body, projectId: env.PROJECT_ID }),\n signal: abortController.signal,\n });\n } catch (error) {\n throw new UnknownCanopyApiError({\n message: `Failed to POST cart operation to ${url}: ${error}`,\n cause: error,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new CanopyApiGatewayError({\n message: `Cart operation ${path} failed: ${response.status} ${response.statusText} \u2014 ${text}`,\n statusCode: response.status,\n });\n }\n\n // Legacy cast - may or may not be necessary\n // eslint-disable-next-line replo/no-as-cast\n const json = (await response.json()) as { data: unknown };\n return cartSchema.parse(json.data);\n }\n\n async cartGet({ cartId }: { cartId: string }): Promise<Cart | null> {\n try {\n return await this._cartPost(\"/get\", { cartId });\n } catch (error) {\n if (error instanceof CanopyApiGatewayError && error.statusCode === 404) {\n return null;\n }\n throw error;\n }\n }\n\n async cartCreate(options?: {\n lines?: {\n merchandiseId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n discountCodes?: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/create\", {\n lines: options?.lines,\n discountCodes: options?.discountCodes,\n });\n }\n\n async cartAddLines({\n cartId,\n lines,\n }: {\n cartId: string;\n lines: {\n merchandiseId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n }): Promise<Cart> {\n return this._cartPost(\"/add-lines\", { cartId, lines });\n }\n\n async cartUpdateLines({\n cartId,\n lines,\n }: {\n cartId: string;\n lines: {\n lineId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n }): Promise<Cart> {\n return this._cartPost(\"/update-lines\", { cartId, lines });\n }\n\n async cartRemoveLines({\n cartId,\n lineIds,\n }: {\n cartId: string;\n lineIds: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/remove-lines\", { cartId, lineIds });\n }\n\n async cartUpdateDiscountCodes({\n cartId,\n discountCodes,\n }: {\n cartId: string;\n discountCodes: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/update-discount-codes\", { cartId, discountCodes });\n }\n}\n\nexport async function getCanopyApiGateway() {\n return CanopyApiGateway.setup();\n}\n"],
5
- "mappings": "AAEA,SAAS,SAAS;AAElB,SAAS,kBAAkB;AAC3B,SAAS,cAAc;AACvB,SAAS,mBAAmB;AAC5B,SAAS,oBAAoB;AAStB,MAAM,8BAA8B,YAAY;AAAA,EACrD;AAAA,EAEA,YAAY,QAAqC;AAC/C,UAAM,qBAAqB,OAAO,cAAc;AAChD,UAAM;AAAA,MACJ,SAAS,OAAO;AAAA,MAChB,OAAO,OAAO;AAAA,MACd,gBAAgB;AAAA,QACd,GAAG,OAAO;AAAA,QACV,YAAY;AAAA,MACd;AAAA,IACF,CAAC;AACD,SAAK,aAAa;AAAA,EACpB;AACF;AAEA,MAAM,8BAA8B,sBAAsB;AAAA,EACxD,YAAY,QAAyD;AACnE,UAAM,EAAE,GAAG,QAAQ,YAAY,IAAI,CAAC;AAAA,EACtC;AACF;AAEA,MAAM,6BAA6B,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,QAAQ;AAAA,EAChB,MAAM,EACH,OAAO;AAAA,IACN,OAAO,EAAE,OAAO;AAAA,MACd,KAAK,EAAE,QAAQ;AAAA,MACf,YAAY,EAAE,OAAO;AAAA,IACvB,CAAC;AAAA,EACH,CAAC,EACA,SAAS;AACd,CAAC;AAED,MAAM,iBAAiB;AAAA,EACb;AAAA,EACA;AAAA,EACR,YAAY,SAAiB;AAC3B,SAAK,UAAU;AACf,SAAK,UAAU,aAAa,OAAO;AAAA,EACrC;AAAA,EAEA,OAAwB,+BAA+B;AAAA,EAEvD,aAAa,QAAQ;AACnB,UAAM,MAAM,MAAM,OAAO;AAEzB,WAAO,IAAI,iBAAiB,IAAI,eAAe;AAAA,EACjD;AAAA,EAEA,MAAc,MAAM,KAAa,MAAe;AAC9C,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,YAAY,WAAW,MAAM;AACjC,sBAAgB,MAAM;AAAA,IACxB,GAAG,iBAAiB,4BAA4B;AAEhD,QAAI;AACF,aAAO,MAAM,MAAM,KAAK;AAAA,QACtB,QAAQ;AAAA,QACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,QAC9C,MAAM,KAAK,UAAU,IAAI;AAAA,QACzB,QAAQ,gBAAgB;AAAA,MAC1B,CAAC;AAAA,IACH,SAAS,OAAO;AACd,UAAI,UAAU,mCAAmC,GAAG,KAAK,KAAK;AAC9D,UAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,mBAAW;AAAA;AAAA,WAAgB,KAAK,OAAO;AAAA;AAAA,MACzC;AACA,YAAM,IAAI,sBAAsB,EAAE,SAAS,OAAO,MAAM,CAAC;AAAA,IAC3D,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAc,KAAK,KAAa;AAC9B,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,YAAY,WAAW,MAAM;AACjC,sBAAgB,MAAM;AAAA,IACxB,GAAG,iBAAiB,4BAA4B;AAEhD,QAAI;AACF,aAAO,MAAM,MAAM,KAAK,EAAE,QAAQ,gBAAgB,OAAO,CAAC;AAAA,IAC5D,SAAS,OAAO;AACd,UAAI,UAAU,oCAAoC,GAAG,KAAK,KAAK;AAC/D,UAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,mBAAW;AAAA;AAAA,WAAgB,KAAK,OAAO;AAAA;AAAA,MACzC;AACA,YAAM,IAAI,sBAAsB,EAAE,SAAS,OAAO,MAAM,CAAC;AAAA,IAC3D,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAAA,EACF;AAAA,EAEA,MAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,EACF,GAGG;AACD,UAAM,MAAM,MAAM,OAAO;AACzB,UAAM,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,OAAO,0BAA0B;AAAA,MACzE,WAAW,IAAI;AAAA,MACf;AAAA,MACA;AAAA;AAAA;AAAA;AAAA,MAIA,yBAAyB,QAAQ,QAAQ,IAAI,gBAAgB;AAAA,IAC/D,CAAC;AAED,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,YAAY,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACtD,YAAM,IAAI,sBAAsB;AAAA,QAC9B,SAAS,2BAA2B,SAAS,KAAK,SAAS,MAAM,IAAI,SAAS,UAAU,GAAG,YAAY,WAAM,SAAS,KAAK,EAAE;AAAA,QAC7H,YAAY,SAAS;AAAA,MACvB,CAAC;AAAA,IACH;AAEA,WAAO,2BAA2B,MAAM,MAAM,SAAS,KAAK,CAAC;AAAA,EAC/D;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,UACZ,MACA,MACe;AACf,UAAM,MAAM,MAAM,OAAO;AACzB,UAAM,MAAM,GAAG,KAAK,OAAO,eAAe,IAAI;AAE9C,UAAM,kBAAkB,IAAI,gBAAgB;AAC5C,UAAM,YAAY,WAAW,MAAM;AACjC,sBAAgB,MAAM;AAAA,IACxB,GAAG,iBAAiB,4BAA4B;AAEhD,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,KAAK;AAAA,QAC1B,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,EAAE,GAAG,MAAM,WAAW,IAAI,WAAW,CAAC;AAAA,QAC3D,QAAQ,gBAAgB;AAAA,MAC1B,CAAC;AAAA,IACH,SAAS,OAAO;AACd,YAAM,IAAI,sBAAsB;AAAA,QAC9B,SAAS,oCAAoC,GAAG,KAAK,KAAK;AAAA,QAC1D,OAAO;AAAA,MACT,CAAC;AAAA,IACH,UAAE;AACA,mBAAa,SAAS;AAAA,IACxB;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,YAAM,IAAI,sBAAsB;AAAA,QAC9B,SAAS,kBAAkB,IAAI,YAAY,SAAS,MAAM,IAAI,SAAS,UAAU,WAAM,IAAI;AAAA,QAC3F,YAAY,SAAS;AAAA,MACvB,CAAC;AAAA,IACH;AAIA,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO,WAAW,MAAM,KAAK,IAAI;AAAA,EACnC;AAAA,EAEA,MAAM,QAAQ,EAAE,OAAO,GAA6C;AAClE,QAAI;AACF,aAAO,MAAM,KAAK,UAAU,QAAQ,EAAE,OAAO,CAAC;AAAA,IAChD,SAAS,OAAO;AACd,UAAI,iBAAiB,yBAAyB,MAAM,eAAe,KAAK;AACtE,eAAO;AAAA,MACT;AACA,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,MAAM,WAAW,SAQC;AAChB,WAAO,KAAK,UAAU,WAAW;AAAA,MAC/B,OAAO,SAAS;AAAA,MAChB,eAAe,SAAS;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA,EAEA,MAAM,aAAa;AAAA,IACjB;AAAA,IACA;AAAA,EACF,GAQkB;AAChB,WAAO,KAAK,UAAU,cAAc,EAAE,QAAQ,MAAM,CAAC;AAAA,EACvD;AAAA,EAEA,MAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,EACF,GAQkB;AAChB,WAAO,KAAK,UAAU,iBAAiB,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC1D;AAAA,EAEA,MAAM,gBAAgB;AAAA,IACpB;AAAA,IACA;AAAA,EACF,GAGkB;AAChB,WAAO,KAAK,UAAU,iBAAiB,EAAE,QAAQ,QAAQ,CAAC;AAAA,EAC5D;AAAA,EAEA,MAAM,wBAAwB;AAAA,IAC5B;AAAA,IACA;AAAA,EACF,GAGkB;AAChB,WAAO,KAAK,UAAU,0BAA0B,EAAE,QAAQ,cAAc,CAAC;AAAA,EAC3E;AACF;AAEA,eAAsB,sBAAsB;AAC1C,SAAO,iBAAiB,MAAM;AAChC;",
4
+ "sourcesContent": ["import type { Cart } from \"../cart/cart-schema\";\n\nimport { z } from \"zod\";\n\nimport { cartSchema } from \"../cart/cart-schema\";\nimport { getEnv } from \"../env\";\nimport { CanopyError } from \"../lib/canopy-error\";\nimport { buildBaseUrl } from \"../lib/url-utils\";\n\ntype CanopyApiGatewayErrorConfig = {\n message: string;\n statusCode?: number;\n cause?: unknown;\n additionalData?: Record<string, unknown>;\n};\n\nexport class CanopyApiGatewayError extends CanopyError {\n statusCode: number;\n\n constructor(config: CanopyApiGatewayErrorConfig) {\n const resolvedStatusCode = config.statusCode ?? 500;\n super({\n message: config.message,\n cause: config.cause,\n additionalData: {\n ...config.additionalData,\n statusCode: resolvedStatusCode,\n },\n });\n this.statusCode = resolvedStatusCode;\n }\n}\n\nclass UnknownCanopyApiError extends CanopyApiGatewayError {\n constructor(config: Omit<CanopyApiGatewayErrorConfig, \"statusCode\">) {\n super({ ...config, statusCode: 502 });\n }\n}\n\nconst loaderInvokeResponseSchema = z.object({\n data: z.unknown(),\n meta: z\n .object({\n cache: z.object({\n hit: z.boolean(),\n ttlSeconds: z.number(),\n }),\n })\n .optional(),\n});\n\nconst reconciledCartLineSchema = z.discriminatedUnion(\"status\", [\n z.object({\n variantId: z.string(),\n status: z.literal(\"purchasable\"),\n unitPriceMinor: z.number().int(),\n compareAtPriceMinor: z.number().int().nullable(),\n currencyCode: z.string(),\n purchasableQuantity: z.number().int().min(0),\n }),\n z.object({\n variantId: z.string(),\n status: z.literal(\"unavailable\"),\n }),\n]);\n\nconst reconcileCartLinesResponseSchema = z.object({\n lines: z.array(reconciledCartLineSchema),\n});\n\nexport type ReconciledCartLine = z.infer<typeof reconciledCartLineSchema>;\n\nclass CanopyApiGateway {\n private apiHost: string;\n private baseUrl: string;\n constructor(apiHost: string) {\n this.apiHost = apiHost;\n this.baseUrl = buildBaseUrl(apiHost);\n }\n\n private static readonly REQUEST_TIMEOUT_MILLISECONDS = 10_000;\n\n static async setup() {\n const env = await getEnv();\n\n return new CanopyApiGateway(env.CANOPY_API_HOST);\n }\n\n private async _post(url: string, body: unknown) {\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n try {\n return await fetch(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify(body),\n signal: abortController.signal,\n });\n } catch (error) {\n let message = `Failed to POST to canopy-api at ${url}: ${error}`;\n if (process.env.NODE_ENV !== \"production\") {\n message += `\\n\\napiHost: ${this.apiHost}\\nIf you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;\n }\n throw new UnknownCanopyApiError({ message, cause: error });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n private async _get(url: string) {\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n try {\n return await fetch(url, { signal: abortController.signal });\n } catch (error) {\n let message = `Failed to GET from canopy-api at ${url}: ${error}`;\n if (process.env.NODE_ENV !== \"production\") {\n message += `\\n\\napiHost: ${this.apiHost}\\nIf you're running locally, make sure the canopy-api cloudflare worker is running. You may need to restart the dev server.`;\n }\n throw new UnknownCanopyApiError({ message, cause: error });\n } finally {\n clearTimeout(timeoutId);\n }\n }\n\n async invokeLoader({\n loaderKey,\n args,\n }: {\n loaderKey: string;\n args: Record<string, unknown>;\n }) {\n const env = await getEnv();\n const response = await this._post(`${this.baseUrl}/api/v1/loaders/invoke`, {\n projectId: env.PROJECT_ID,\n loaderKey,\n args,\n // The builder preview must render a product the merchant hasn't launched\n // yet; a published storefront must not. Only the sandbox dev server sets\n // IS_REPLO_SANDBOX, so it is what separates the two.\n includeInactiveProducts: Boolean(process.env.IS_REPLO_SANDBOX),\n });\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n throw new CanopyApiGatewayError({\n message: `Failed to invoke loader ${loaderKey}: ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : \"\"}`,\n statusCode: response.status,\n });\n }\n\n return loaderInvokeResponseSchema.parse(await response.json());\n }\n\n /**\n * The catalog's current answer for a set of cookie-cart lines: per line,\n * the live price and how many units are actually purchasable. Used to\n * reconcile the client-persisted cart, which otherwise never re-reads the\n * catalog facts it snapshotted at add-to-cart time.\n */\n async reconcileCartLines({\n lines,\n }: {\n lines: { variantId: string; quantity: number }[];\n }): Promise<ReconciledCartLine[]> {\n const env = await getEnv();\n const response = await this._post(\n `${this.baseUrl}/api/v1/product-catalog/reconcile-cart-lines`,\n { projectId: env.PROJECT_ID, lines },\n );\n\n if (!response.ok) {\n const errorBody = await response.text().catch(() => \"\");\n throw new CanopyApiGatewayError({\n message: `Failed to reconcile cart lines: ${response.status} ${response.statusText}${errorBody ? ` \u2014 ${errorBody}` : \"\"}`,\n statusCode: response.status,\n });\n }\n\n return reconcileCartLinesResponseSchema.parse(await response.json()).lines;\n }\n\n // ---------------------------------------------------------------------------\n // Cart operations (canopy-api-backed ExternalCart)\n // ---------------------------------------------------------------------------\n\n private async _cartPost(\n path: string,\n body: Record<string, unknown>,\n ): Promise<Cart> {\n const env = await getEnv();\n const url = `${this.baseUrl}/api/v1/cart${path}`;\n\n const abortController = new AbortController();\n const timeoutId = setTimeout(() => {\n abortController.abort();\n }, CanopyApiGateway.REQUEST_TIMEOUT_MILLISECONDS);\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ ...body, projectId: env.PROJECT_ID }),\n signal: abortController.signal,\n });\n } catch (error) {\n throw new UnknownCanopyApiError({\n message: `Failed to POST cart operation to ${url}: ${error}`,\n cause: error,\n });\n } finally {\n clearTimeout(timeoutId);\n }\n\n if (!response.ok) {\n const text = await response.text().catch(() => \"\");\n throw new CanopyApiGatewayError({\n message: `Cart operation ${path} failed: ${response.status} ${response.statusText} \u2014 ${text}`,\n statusCode: response.status,\n });\n }\n\n // Legacy cast - may or may not be necessary\n // eslint-disable-next-line replo/no-as-cast\n const json = (await response.json()) as { data: unknown };\n return cartSchema.parse(json.data);\n }\n\n async cartGet({ cartId }: { cartId: string }): Promise<Cart | null> {\n try {\n return await this._cartPost(\"/get\", { cartId });\n } catch (error) {\n if (error instanceof CanopyApiGatewayError && error.statusCode === 404) {\n return null;\n }\n throw error;\n }\n }\n\n async cartCreate(options?: {\n lines?: {\n merchandiseId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n discountCodes?: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/create\", {\n lines: options?.lines,\n discountCodes: options?.discountCodes,\n });\n }\n\n async cartAddLines({\n cartId,\n lines,\n }: {\n cartId: string;\n lines: {\n merchandiseId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n }): Promise<Cart> {\n return this._cartPost(\"/add-lines\", { cartId, lines });\n }\n\n async cartUpdateLines({\n cartId,\n lines,\n }: {\n cartId: string;\n lines: {\n lineId: string;\n quantity: number;\n attributes?: { key: string; value: string }[];\n sellingPlanId?: string | null;\n }[];\n }): Promise<Cart> {\n return this._cartPost(\"/update-lines\", { cartId, lines });\n }\n\n async cartRemoveLines({\n cartId,\n lineIds,\n }: {\n cartId: string;\n lineIds: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/remove-lines\", { cartId, lineIds });\n }\n\n async cartUpdateDiscountCodes({\n cartId,\n discountCodes,\n }: {\n cartId: string;\n discountCodes: string[];\n }): Promise<Cart> {\n return this._cartPost(\"/update-discount-codes\", { cartId, discountCodes });\n }\n}\n\nexport async function getCanopyApiGateway() {\n return CanopyApiGateway.setup();\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,7 +1,7 @@
1
1
  export const BUILD_METADATA = {
2
2
  "packageName": "@replohq/sdk",
3
- "version": "1.4.1",
3
+ "version": "1.6.0",
4
4
  "branch": "HEAD",
5
- "commit": "1c91f9376d0e3d2c43e75b876a9c6a184969c874",
6
- "builtAt": "2026-09-08T22:17:09.451Z"
5
+ "commit": "4864322a9ff1f3dd385fee9bc0010f4535520cbb",
6
+ "builtAt": "2026-09-10T21:47:40.227Z"
7
7
  };
@@ -0,0 +1,12 @@
1
+ import type { ReactNode } from "react";
2
+ import { z } from "zod";
3
+ import { judgemeProductReviewAggregateLoaderOutputSchema } from "./loader-schemas";
4
+ type JudgemeReviewAggregateData = z.infer<typeof judgemeProductReviewAggregateLoaderOutputSchema>;
5
+ type JudgemeReviewAggregateLoaderProps = {
6
+ loaderKey: string;
7
+ productId: string;
8
+ children: (data: JudgemeReviewAggregateData) => ReactNode;
9
+ fallback?: ReactNode;
10
+ };
11
+ export declare function JudgemeReviewAggregateLoader({ loaderKey, productId, children, fallback, }: JudgemeReviewAggregateLoaderProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
@@ -0,0 +1,50 @@
1
+ "use client";
2
+ import { Fragment, jsx } from "react/jsx-runtime";
3
+ import { Suspense } from "react";
4
+ import { useSuspenseQuery } from "@tanstack/react-query";
5
+ import { invokeLoader } from "./invoke-loader";
6
+ import { LoaderErrorBoundary } from "./loader-error-boundary";
7
+ import { judgemeProductReviewAggregateLoaderOutputSchema } from "./loader-schemas";
8
+ function JudgemeReviewAggregateLoaderInner({
9
+ loaderKey,
10
+ productId,
11
+ children
12
+ }) {
13
+ const { data } = useSuspenseQuery({
14
+ queryKey: [loaderKey, { productId }],
15
+ queryFn: async () => {
16
+ const loadedData = await invokeLoader({
17
+ loaderKey,
18
+ args: { productId }
19
+ });
20
+ return judgemeProductReviewAggregateLoaderOutputSchema.parse(loadedData);
21
+ }
22
+ });
23
+ return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
24
+ }
25
+ function JudgemeReviewAggregateLoader({
26
+ loaderKey,
27
+ productId,
28
+ children,
29
+ fallback
30
+ }) {
31
+ return /* @__PURE__ */ jsx(
32
+ LoaderErrorBoundary,
33
+ {
34
+ fallback: fallback ?? null,
35
+ resetKey: `${loaderKey}:${productId}`,
36
+ children: /* @__PURE__ */ jsx(Suspense, { fallback: fallback ?? null, children: /* @__PURE__ */ jsx(
37
+ JudgemeReviewAggregateLoaderInner,
38
+ {
39
+ loaderKey,
40
+ productId,
41
+ children
42
+ }
43
+ ) })
44
+ }
45
+ );
46
+ }
47
+ export {
48
+ JudgemeReviewAggregateLoader
49
+ };
50
+ //# sourceMappingURL=judgeme-review-aggregate-loader.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../loaders/judgeme-review-aggregate-loader.tsx"],
4
+ "sourcesContent": ["\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { Suspense } from \"react\";\n\nimport { useSuspenseQuery } from \"@tanstack/react-query\";\nimport { z } from \"zod\";\n\nimport { invokeLoader } from \"./invoke-loader\";\nimport { LoaderErrorBoundary } from \"./loader-error-boundary\";\nimport { judgemeProductReviewAggregateLoaderOutputSchema } from \"./loader-schemas\";\n\ntype JudgemeReviewAggregateData = z.infer<\n typeof judgemeProductReviewAggregateLoaderOutputSchema\n>;\n\ntype JudgemeReviewAggregateLoaderProps = {\n loaderKey: string;\n productId: string;\n children: (data: JudgemeReviewAggregateData) => ReactNode;\n fallback?: ReactNode;\n};\n\nfunction JudgemeReviewAggregateLoaderInner({\n loaderKey,\n productId,\n children,\n}: Omit<JudgemeReviewAggregateLoaderProps, \"fallback\">) {\n const { data } = useSuspenseQuery({\n queryKey: [loaderKey, { productId }],\n queryFn: async () => {\n const loadedData = await invokeLoader({\n loaderKey,\n args: { productId },\n });\n return judgemeProductReviewAggregateLoaderOutputSchema.parse(loadedData);\n },\n });\n\n return <>{children(data)}</>;\n}\n\nexport function JudgemeReviewAggregateLoader({\n loaderKey,\n productId,\n children,\n fallback,\n}: JudgemeReviewAggregateLoaderProps) {\n return (\n <LoaderErrorBoundary\n fallback={fallback ?? null}\n resetKey={`${loaderKey}:${productId}`}\n >\n <Suspense fallback={fallback ?? null}>\n <JudgemeReviewAggregateLoaderInner\n loaderKey={loaderKey}\n productId={productId}\n >\n {children}\n </JudgemeReviewAggregateLoaderInner>\n </Suspense>\n </LoaderErrorBoundary>\n );\n}\n"],
5
+ "mappings": ";AAwCS;AApCT,SAAS,gBAAgB;AAEzB,SAAS,wBAAwB;AAGjC,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,uDAAuD;AAahE,SAAS,kCAAkC;AAAA,EACzC;AAAA,EACA;AAAA,EACA;AACF,GAAwD;AACtD,QAAM,EAAE,KAAK,IAAI,iBAAiB;AAAA,IAChC,UAAU,CAAC,WAAW,EAAE,UAAU,CAAC;AAAA,IACnC,SAAS,YAAY;AACnB,YAAM,aAAa,MAAM,aAAa;AAAA,QACpC;AAAA,QACA,MAAM,EAAE,UAAU;AAAA,MACpB,CAAC;AACD,aAAO,gDAAgD,MAAM,UAAU;AAAA,IACzE;AAAA,EACF,CAAC;AAED,SAAO,gCAAG,mBAAS,IAAI,GAAE;AAC3B;AAEO,SAAS,6BAA6B;AAAA,EAC3C;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAsC;AACpC,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,YAAY;AAAA,MACtB,UAAU,GAAG,SAAS,IAAI,SAAS;AAAA,MAEnC,8BAAC,YAAS,UAAU,YAAY,MAC9B;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UAEC;AAAA;AAAA,MACH,GACF;AAAA;AAAA,EACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,14 @@
1
+ import type { ReactNode } from "react";
2
+ import { z } from "zod";
3
+ import { judgemeProductReviewsLoaderOutputSchema } from "./loader-schemas";
4
+ type JudgemeReviewsData = z.infer<typeof judgemeProductReviewsLoaderOutputSchema>;
5
+ type JudgemeReviewsLoaderProps = {
6
+ loaderKey: string;
7
+ productId: string;
8
+ perPage?: number;
9
+ page?: number;
10
+ children: (data: JudgemeReviewsData) => ReactNode;
11
+ fallback?: ReactNode;
12
+ };
13
+ export declare function JudgemeReviewsLoader({ loaderKey, productId, perPage, page, children, fallback, }: JudgemeReviewsLoaderProps): import("react/jsx-runtime").JSX.Element;
14
+ export {};
@@ -0,0 +1,65 @@
1
+ "use client";
2
+ import { Fragment, jsx } from "react/jsx-runtime";
3
+ import { Suspense } from "react";
4
+ import { useSuspenseQuery } from "@tanstack/react-query";
5
+ import { invokeLoader } from "./invoke-loader";
6
+ import { LoaderErrorBoundary } from "./loader-error-boundary";
7
+ import { judgemeProductReviewsLoaderOutputSchema } from "./loader-schemas";
8
+ function buildReviewsArgs({
9
+ productId,
10
+ perPage,
11
+ page
12
+ }) {
13
+ return {
14
+ productId,
15
+ ...perPage !== void 0 ? { perPage } : {},
16
+ ...page !== void 0 ? { page } : {}
17
+ };
18
+ }
19
+ function JudgemeReviewsLoaderInner({
20
+ loaderKey,
21
+ productId,
22
+ perPage,
23
+ page,
24
+ children
25
+ }) {
26
+ const args = buildReviewsArgs({ productId, perPage, page });
27
+ const { data } = useSuspenseQuery({
28
+ queryKey: [loaderKey, args],
29
+ queryFn: async () => {
30
+ const loadedData = await invokeLoader({ loaderKey, args });
31
+ return judgemeProductReviewsLoaderOutputSchema.parse(loadedData);
32
+ }
33
+ });
34
+ return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
35
+ }
36
+ function JudgemeReviewsLoader({
37
+ loaderKey,
38
+ productId,
39
+ perPage,
40
+ page,
41
+ children,
42
+ fallback
43
+ }) {
44
+ return /* @__PURE__ */ jsx(
45
+ LoaderErrorBoundary,
46
+ {
47
+ fallback: fallback ?? null,
48
+ resetKey: `${loaderKey}:${productId}:${perPage ?? ""}:${page ?? ""}`,
49
+ children: /* @__PURE__ */ jsx(Suspense, { fallback: fallback ?? null, children: /* @__PURE__ */ jsx(
50
+ JudgemeReviewsLoaderInner,
51
+ {
52
+ loaderKey,
53
+ productId,
54
+ perPage,
55
+ page,
56
+ children
57
+ }
58
+ ) })
59
+ }
60
+ );
61
+ }
62
+ export {
63
+ JudgemeReviewsLoader
64
+ };
65
+ //# sourceMappingURL=judgeme-reviews-loader.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../loaders/judgeme-reviews-loader.tsx"],
4
+ "sourcesContent": ["\"use client\";\n\nimport type { ReactNode } from \"react\";\n\nimport { Suspense } from \"react\";\n\nimport { useSuspenseQuery } from \"@tanstack/react-query\";\nimport { z } from \"zod\";\n\nimport { invokeLoader } from \"./invoke-loader\";\nimport { LoaderErrorBoundary } from \"./loader-error-boundary\";\nimport { judgemeProductReviewsLoaderOutputSchema } from \"./loader-schemas\";\n\ntype JudgemeReviewsData = z.infer<\n typeof judgemeProductReviewsLoaderOutputSchema\n>;\n\ntype JudgemeReviewsLoaderProps = {\n loaderKey: string;\n productId: string;\n perPage?: number;\n page?: number;\n children: (data: JudgemeReviewsData) => ReactNode;\n fallback?: ReactNode;\n};\n\n// Only forward pagination args when set so the queryKey and prefetch args\n// match (a prefetch that omits page/perPage must hydrate a component that also\n// omits them). React Query keys are structural, so undefined entries would\n// break the prefetch \u2192 client cache handoff.\nfunction buildReviewsArgs({\n productId,\n perPage,\n page,\n}: {\n productId: string;\n perPage?: number;\n page?: number;\n}) {\n return {\n productId,\n ...(perPage !== undefined ? { perPage } : {}),\n ...(page !== undefined ? { page } : {}),\n };\n}\n\nfunction JudgemeReviewsLoaderInner({\n loaderKey,\n productId,\n perPage,\n page,\n children,\n}: Omit<JudgemeReviewsLoaderProps, \"fallback\">) {\n const args = buildReviewsArgs({ productId, perPage, page });\n const { data } = useSuspenseQuery({\n queryKey: [loaderKey, args],\n queryFn: async () => {\n const loadedData = await invokeLoader({ loaderKey, args });\n return judgemeProductReviewsLoaderOutputSchema.parse(loadedData);\n },\n });\n\n return <>{children(data)}</>;\n}\n\nexport function JudgemeReviewsLoader({\n loaderKey,\n productId,\n perPage,\n page,\n children,\n fallback,\n}: JudgemeReviewsLoaderProps) {\n return (\n <LoaderErrorBoundary\n fallback={fallback ?? null}\n resetKey={`${loaderKey}:${productId}:${perPage ?? \"\"}:${page ?? \"\"}`}\n >\n <Suspense fallback={fallback ?? null}>\n <JudgemeReviewsLoaderInner\n loaderKey={loaderKey}\n productId={productId}\n perPage={perPage}\n page={page}\n >\n {children}\n </JudgemeReviewsLoaderInner>\n </Suspense>\n </LoaderErrorBoundary>\n );\n}\n"],
5
+ "mappings": ";AA8DS;AA1DT,SAAS,gBAAgB;AAEzB,SAAS,wBAAwB;AAGjC,SAAS,oBAAoB;AAC7B,SAAS,2BAA2B;AACpC,SAAS,+CAA+C;AAmBxD,SAAS,iBAAiB;AAAA,EACxB;AAAA,EACA;AAAA,EACA;AACF,GAIG;AACD,SAAO;AAAA,IACL;AAAA,IACA,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC3C,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,EACvC;AACF;AAEA,SAAS,0BAA0B;AAAA,EACjC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAgD;AAC9C,QAAM,OAAO,iBAAiB,EAAE,WAAW,SAAS,KAAK,CAAC;AAC1D,QAAM,EAAE,KAAK,IAAI,iBAAiB;AAAA,IAChC,UAAU,CAAC,WAAW,IAAI;AAAA,IAC1B,SAAS,YAAY;AACnB,YAAM,aAAa,MAAM,aAAa,EAAE,WAAW,KAAK,CAAC;AACzD,aAAO,wCAAwC,MAAM,UAAU;AAAA,IACjE;AAAA,EACF,CAAC;AAED,SAAO,gCAAG,mBAAS,IAAI,GAAE;AAC3B;AAEO,SAAS,qBAAqB;AAAA,EACnC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA8B;AAC5B,SACE;AAAA,IAAC;AAAA;AAAA,MACC,UAAU,YAAY;AAAA,MACtB,UAAU,GAAG,SAAS,IAAI,SAAS,IAAI,WAAW,EAAE,IAAI,QAAQ,EAAE;AAAA,MAElE,8BAAC,YAAS,UAAU,YAAY,MAC9B;AAAA,QAAC;AAAA;AAAA,UACC;AAAA,UACA;AAAA,UACA;AAAA,UACA;AAAA,UAEC;AAAA;AAAA,MACH,GACF;AAAA;AAAA,EACF;AAEJ;",
6
+ "names": []
7
+ }
@@ -29,6 +29,8 @@ export declare const DATA_LOADER_KEYS: {
29
29
  readonly REVIEWS_IO_PRODUCT_REVIEW_AGGREGATE: "reviews_io.product_review_aggregate";
30
30
  readonly LOOX_PRODUCT_REVIEWS: "loox.product_reviews";
31
31
  readonly LOOX_PRODUCT_REVIEW_AGGREGATE: "loox.product_review_aggregate";
32
+ readonly JUDGEME_PRODUCT_REVIEWS: "judgeme.product_reviews";
33
+ readonly JUDGEME_PRODUCT_REVIEW_AGGREGATE: "judgeme.product_review_aggregate";
32
34
  readonly STATSIG_EXPERIMENT: "statsig.experiment";
33
35
  readonly CONTENTFUL_ENTRY: "contentful.entry";
34
36
  readonly CONTENTFUL_ENTRIES: "contentful.entries";
@@ -18,6 +18,8 @@ const DATA_LOADER_KEYS = {
18
18
  REVIEWS_IO_PRODUCT_REVIEW_AGGREGATE: "reviews_io.product_review_aggregate",
19
19
  LOOX_PRODUCT_REVIEWS: "loox.product_reviews",
20
20
  LOOX_PRODUCT_REVIEW_AGGREGATE: "loox.product_review_aggregate",
21
+ JUDGEME_PRODUCT_REVIEWS: "judgeme.product_reviews",
22
+ JUDGEME_PRODUCT_REVIEW_AGGREGATE: "judgeme.product_review_aggregate",
21
23
  STATSIG_EXPERIMENT: "statsig.experiment",
22
24
  CONTENTFUL_ENTRY: "contentful.entry",
23
25
  CONTENTFUL_ENTRIES: "contentful.entries"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../loaders/loader-keys.ts"],
4
- "sourcesContent": ["// NOTE (Ryan, 2026-07-23, REPL-27611): Canonical loader-key constants for the\n// published-page surface. the SDK owns this copy so nothing shipped to a\n// generated site imports from `schemas`. The backend keeps its own copy in\n// `schemas/loaderKeys.ts` (consumed by the integration registry + tool\n// metadata, which cannot depend on the SDK without a cycle); the two are\n// kept in lockstep by the drift guard in `loader-keys.spec.ts`.\n\n/**\n * Shared data-loader key constants. Loader components and prefetch queries\n * import from here so the strings are never duplicated across a generated site.\n *\n * These keys serve double duty: they identify which backend loader to invoke\n * AND are used as React Query cache key prefixes. Loader components internally\n * construct queryKey as `[loaderKey, args]`.\n *\n * Naming convention: `<integration>.<objectType>` or\n * `<integration>.<objectType>_<qualifier>`.\n */\nexport const DATA_LOADER_KEYS = {\n SHOPIFY_PRODUCT: \"shopify_storefront.product\",\n SHOPIFY_COLLECTION: \"shopify_storefront.collection\",\n SHOPIFY_COLLECTION_PRODUCTS: \"shopify_storefront.collection_products\",\n SHOPIFY_METAOBJECT: \"shopify_storefront.metaobject\",\n SHOPIFY_METAOBJECTS: \"shopify_storefront.metaobjects\",\n REPLO_PRODUCT: \"replo_products.product\",\n OKENDO_PRODUCT_REVIEWS: \"okendo.product_reviews\",\n OKENDO_PRODUCT_REVIEW_AGGREGATE: \"okendo.product_review_aggregate\",\n REBUY_RECOMMENDATIONS: \"rebuy.recommendations\",\n LEVANTA_PRODUCT: \"levanta_products.product\",\n SMILE_IO_VIP_TIERS: \"smile_io.vip_tiers\",\n SMILE_IO_EARNING_RULES: \"smile_io.earning_rules\",\n SMILE_IO_REWARDS: \"smile_io.rewards\",\n YOTPO_PRODUCT_REVIEWS: \"yotpo.product_reviews\",\n YOTPO_PRODUCT_REVIEW_AGGREGATE: \"yotpo.product_review_aggregate\",\n REVIEWS_IO_PRODUCT_REVIEWS: \"reviews_io.product_reviews\",\n REVIEWS_IO_PRODUCT_REVIEW_AGGREGATE: \"reviews_io.product_review_aggregate\",\n LOOX_PRODUCT_REVIEWS: \"loox.product_reviews\",\n LOOX_PRODUCT_REVIEW_AGGREGATE: \"loox.product_review_aggregate\",\n STATSIG_EXPERIMENT: \"statsig.experiment\",\n CONTENTFUL_ENTRY: \"contentful.entry\",\n CONTENTFUL_ENTRIES: \"contentful.entries\",\n} as const;\n\nexport type DataLoaderKey =\n (typeof DATA_LOADER_KEYS)[keyof typeof DATA_LOADER_KEYS];\n"],
5
- "mappings": "AAkBO,MAAM,mBAAmB;AAAA,EAC9B,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,6BAA6B;AAAA,EAC7B,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,wBAAwB;AAAA,EACxB,iCAAiC;AAAA,EACjC,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,gCAAgC;AAAA,EAChC,4BAA4B;AAAA,EAC5B,qCAAqC;AAAA,EACrC,sBAAsB;AAAA,EACtB,+BAA+B;AAAA,EAC/B,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,oBAAoB;AACtB;",
4
+ "sourcesContent": ["// NOTE (Ryan, 2026-07-23, REPL-27611): Canonical loader-key constants for the\n// published-page surface. the SDK owns this copy so nothing shipped to a\n// generated site imports from `schemas`. The backend keeps its own copy in\n// `schemas/loaderKeys.ts` (consumed by the integration registry + tool\n// metadata, which cannot depend on the SDK without a cycle); the two are\n// kept in lockstep by the drift guard in `loader-keys.spec.ts`.\n\n/**\n * Shared data-loader key constants. Loader components and prefetch queries\n * import from here so the strings are never duplicated across a generated site.\n *\n * These keys serve double duty: they identify which backend loader to invoke\n * AND are used as React Query cache key prefixes. Loader components internally\n * construct queryKey as `[loaderKey, args]`.\n *\n * Naming convention: `<integration>.<objectType>` or\n * `<integration>.<objectType>_<qualifier>`.\n */\nexport const DATA_LOADER_KEYS = {\n SHOPIFY_PRODUCT: \"shopify_storefront.product\",\n SHOPIFY_COLLECTION: \"shopify_storefront.collection\",\n SHOPIFY_COLLECTION_PRODUCTS: \"shopify_storefront.collection_products\",\n SHOPIFY_METAOBJECT: \"shopify_storefront.metaobject\",\n SHOPIFY_METAOBJECTS: \"shopify_storefront.metaobjects\",\n REPLO_PRODUCT: \"replo_products.product\",\n OKENDO_PRODUCT_REVIEWS: \"okendo.product_reviews\",\n OKENDO_PRODUCT_REVIEW_AGGREGATE: \"okendo.product_review_aggregate\",\n REBUY_RECOMMENDATIONS: \"rebuy.recommendations\",\n LEVANTA_PRODUCT: \"levanta_products.product\",\n SMILE_IO_VIP_TIERS: \"smile_io.vip_tiers\",\n SMILE_IO_EARNING_RULES: \"smile_io.earning_rules\",\n SMILE_IO_REWARDS: \"smile_io.rewards\",\n YOTPO_PRODUCT_REVIEWS: \"yotpo.product_reviews\",\n YOTPO_PRODUCT_REVIEW_AGGREGATE: \"yotpo.product_review_aggregate\",\n REVIEWS_IO_PRODUCT_REVIEWS: \"reviews_io.product_reviews\",\n REVIEWS_IO_PRODUCT_REVIEW_AGGREGATE: \"reviews_io.product_review_aggregate\",\n LOOX_PRODUCT_REVIEWS: \"loox.product_reviews\",\n LOOX_PRODUCT_REVIEW_AGGREGATE: \"loox.product_review_aggregate\",\n JUDGEME_PRODUCT_REVIEWS: \"judgeme.product_reviews\",\n JUDGEME_PRODUCT_REVIEW_AGGREGATE: \"judgeme.product_review_aggregate\",\n STATSIG_EXPERIMENT: \"statsig.experiment\",\n CONTENTFUL_ENTRY: \"contentful.entry\",\n CONTENTFUL_ENTRIES: \"contentful.entries\",\n} as const;\n\nexport type DataLoaderKey =\n (typeof DATA_LOADER_KEYS)[keyof typeof DATA_LOADER_KEYS];\n"],
5
+ "mappings": "AAkBO,MAAM,mBAAmB;AAAA,EAC9B,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,6BAA6B;AAAA,EAC7B,oBAAoB;AAAA,EACpB,qBAAqB;AAAA,EACrB,eAAe;AAAA,EACf,wBAAwB;AAAA,EACxB,iCAAiC;AAAA,EACjC,uBAAuB;AAAA,EACvB,iBAAiB;AAAA,EACjB,oBAAoB;AAAA,EACpB,wBAAwB;AAAA,EACxB,kBAAkB;AAAA,EAClB,uBAAuB;AAAA,EACvB,gCAAgC;AAAA,EAChC,4BAA4B;AAAA,EAC5B,qCAAqC;AAAA,EACrC,sBAAsB;AAAA,EACtB,+BAA+B;AAAA,EAC/B,yBAAyB;AAAA,EACzB,kCAAkC;AAAA,EAClC,oBAAoB;AAAA,EACpB,kBAAkB;AAAA,EAClB,oBAAoB;AACtB;",
6
6
  "names": []
7
7
  }
@@ -193,6 +193,7 @@ export declare const variantSchema: z.ZodObject<{
193
193
  id: z.ZodString;
194
194
  title: z.ZodOptional<z.ZodString>;
195
195
  availableForSale: z.ZodOptional<z.ZodBoolean>;
196
+ isLowStock: z.ZodOptional<z.ZodBoolean>;
196
197
  sku: z.ZodOptional<z.ZodNullable<z.ZodString>>;
197
198
  selectedOptions: z.ZodOptional<z.ZodArray<z.ZodObject<{
198
199
  name: z.ZodString;
@@ -250,6 +251,7 @@ export declare const productSchema: z.ZodObject<{
250
251
  id: z.ZodString;
251
252
  title: z.ZodOptional<z.ZodString>;
252
253
  availableForSale: z.ZodOptional<z.ZodBoolean>;
254
+ isLowStock: z.ZodOptional<z.ZodBoolean>;
253
255
  sku: z.ZodOptional<z.ZodNullable<z.ZodString>>;
254
256
  selectedOptions: z.ZodOptional<z.ZodArray<z.ZodObject<{
255
257
  name: z.ZodString;
@@ -380,6 +382,7 @@ export declare const fullVariantSchema: z.ZodObject<{
380
382
  id: z.ZodString;
381
383
  title: z.ZodString;
382
384
  availableForSale: z.ZodBoolean;
385
+ isLowStock: z.ZodOptional<z.ZodBoolean>;
383
386
  sku: z.ZodNullable<z.ZodString>;
384
387
  selectedOptions: z.ZodArray<z.ZodObject<{
385
388
  name: z.ZodString;
@@ -437,6 +440,7 @@ export declare const fullProductSchema: z.ZodObject<{
437
440
  id: z.ZodString;
438
441
  title: z.ZodString;
439
442
  availableForSale: z.ZodBoolean;
443
+ isLowStock: z.ZodOptional<z.ZodBoolean>;
440
444
  sku: z.ZodNullable<z.ZodString>;
441
445
  selectedOptions: z.ZodArray<z.ZodObject<{
442
446
  name: z.ZodString;
@@ -597,6 +601,7 @@ export declare const paginatedProductsSchema: z.ZodObject<{
597
601
  id: z.ZodString;
598
602
  title: z.ZodOptional<z.ZodString>;
599
603
  availableForSale: z.ZodOptional<z.ZodBoolean>;
604
+ isLowStock: z.ZodOptional<z.ZodBoolean>;
600
605
  sku: z.ZodOptional<z.ZodNullable<z.ZodString>>;
601
606
  selectedOptions: z.ZodOptional<z.ZodArray<z.ZodObject<{
602
607
  name: z.ZodString;
@@ -835,6 +840,31 @@ export declare const looxProductReviewAggregateLoaderOutputSchema: z.ZodObject<{
835
840
  averageRating: z.ZodNumber;
836
841
  ratingDistribution: z.ZodRecord<z.ZodString, z.ZodNumber>;
837
842
  }, z.core.$strip>;
843
+ export declare const judgemeReviewLoaderItemSchema: z.ZodObject<{
844
+ id: z.ZodString;
845
+ title: z.ZodNullable<z.ZodString>;
846
+ content: z.ZodNullable<z.ZodString>;
847
+ score: z.ZodNullable<z.ZodNumber>;
848
+ createdAt: z.ZodNullable<z.ZodString>;
849
+ verifiedBuyer: z.ZodBoolean;
850
+ reviewerName: z.ZodNullable<z.ZodString>;
851
+ }, z.core.$strip>;
852
+ export declare const judgemeProductReviewsLoaderOutputSchema: z.ZodObject<{
853
+ reviews: z.ZodArray<z.ZodObject<{
854
+ id: z.ZodString;
855
+ title: z.ZodNullable<z.ZodString>;
856
+ content: z.ZodNullable<z.ZodString>;
857
+ score: z.ZodNullable<z.ZodNumber>;
858
+ createdAt: z.ZodNullable<z.ZodString>;
859
+ verifiedBuyer: z.ZodBoolean;
860
+ reviewerName: z.ZodNullable<z.ZodString>;
861
+ }, z.core.$strip>>;
862
+ }, z.core.$strip>;
863
+ export declare const judgemeProductReviewAggregateLoaderOutputSchema: z.ZodObject<{
864
+ averageScore: z.ZodNumber;
865
+ totalReviews: z.ZodNumber;
866
+ starDistribution: z.ZodRecord<z.ZodString, z.ZodNumber>;
867
+ }, z.core.$strip>;
838
868
  export declare const statsigExperimentLoaderOutputSchema: z.ZodObject<{
839
869
  experimentName: z.ZodString;
840
870
  groupName: z.ZodNullable<z.ZodString>;
@@ -105,6 +105,7 @@ const variantSchema = z.object({
105
105
  id: z.string(),
106
106
  title: z.string().optional(),
107
107
  availableForSale: z.boolean().optional(),
108
+ isLowStock: z.boolean().optional(),
108
109
  sku: z.string().nullish(),
109
110
  selectedOptions: z.array(selectedOptionSchema).optional(),
110
111
  image: imageSchema.nullish(),
@@ -152,6 +153,7 @@ const fullVariantSchema = z.object({
152
153
  id: z.string(),
153
154
  title: z.string(),
154
155
  availableForSale: z.boolean(),
156
+ isLowStock: z.boolean().optional(),
155
157
  sku: z.string().nullable(),
156
158
  selectedOptions: z.array(selectedOptionSchema),
157
159
  image: imageSchema.nullable(),
@@ -271,6 +273,23 @@ const looxProductReviewAggregateLoaderOutputSchema = z.object({
271
273
  averageRating: z.number(),
272
274
  ratingDistribution: z.record(z.string(), z.number())
273
275
  });
276
+ const judgemeReviewLoaderItemSchema = z.object({
277
+ id: z.string(),
278
+ title: z.string().nullable(),
279
+ content: z.string().nullable(),
280
+ score: z.number().nullable(),
281
+ createdAt: z.string().nullable(),
282
+ verifiedBuyer: z.boolean(),
283
+ reviewerName: z.string().nullable()
284
+ });
285
+ const judgemeProductReviewsLoaderOutputSchema = z.object({
286
+ reviews: z.array(judgemeReviewLoaderItemSchema)
287
+ });
288
+ const judgemeProductReviewAggregateLoaderOutputSchema = z.object({
289
+ averageScore: z.number(),
290
+ totalReviews: z.number(),
291
+ starDistribution: z.record(z.string(), z.number())
292
+ });
274
293
  const statsigExperimentLoaderOutputSchema = z.object({
275
294
  experimentName: z.string(),
276
295
  groupName: z.string().nullable().describe("The assigned variant group name (e.g. Control, Test)."),
@@ -342,6 +361,9 @@ export {
342
361
  fullProductSchema,
343
362
  fullVariantSchema,
344
363
  imageSchema,
364
+ judgemeProductReviewAggregateLoaderOutputSchema,
365
+ judgemeProductReviewsLoaderOutputSchema,
366
+ judgemeReviewLoaderItemSchema,
345
367
  levantaProductFullSchema,
346
368
  looxProductReviewAggregateLoaderOutputSchema,
347
369
  looxProductReviewsLoaderOutputSchema,