@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.
- package/_vendor/replo-utils/lib/url.d.ts +5 -1
- package/_vendor/schemas/loaderKeys.mjs +2 -0
- package/cart/cart-actions.js +2 -1
- package/cart/cart-actions.js.map +2 -2
- package/cart/cart-provider.d.ts +15 -3
- package/cart/cart-provider.js +49 -6
- package/cart/cart-provider.js.map +2 -2
- package/cart/cart-types.d.ts +15 -0
- package/cart/cart-types.js.map +1 -1
- package/cart/gateways/cart-gateway.d.ts +7 -0
- package/cart/gateways/cart-gateway.js.map +2 -2
- package/cart/gateways/cookie-cart-gateway.d.ts +9 -0
- package/cart/gateways/cookie-cart-gateway.js +54 -3
- package/cart/gateways/cookie-cart-gateway.js.map +2 -2
- package/cart/gateways/resolver-cart-gateway.js +4 -0
- package/cart/gateways/resolver-cart-gateway.js.map +2 -2
- package/cart/gateways/stripe-cart-gateway.d.ts +14 -0
- package/cart/gateways/stripe-cart-gateway.js +6 -1
- package/cart/gateways/stripe-cart-gateway.js.map +2 -2
- package/cart/hooks/use-add-to-cart.d.ts +3 -1
- package/cart/hooks/use-add-to-cart.js +1 -1
- package/cart/hooks/use-add-to-cart.js.map +2 -2
- package/cart/utils/cart-adjustments.d.ts +29 -0
- package/cart/utils/cart-adjustments.js +81 -0
- package/cart/utils/cart-adjustments.js.map +7 -0
- package/cart/utils/cart-utils.d.ts +14 -0
- package/cart/utils/cart-utils.js +43 -0
- package/cart/utils/cart-utils.js.map +2 -2
- package/cart/utils/cookie-cart-persistence.d.ts +4 -2
- package/cart/utils/cookie-cart-persistence.js +35 -21
- package/cart/utils/cookie-cart-persistence.js.map +3 -3
- package/gateways/canopy-api.d.ts +25 -0
- package/gateways/canopy-api.js +40 -0
- package/gateways/canopy-api.js.map +2 -2
- package/lib/buildMetadata.js +3 -3
- package/loaders/judgeme-review-aggregate-loader.d.ts +12 -0
- package/loaders/judgeme-review-aggregate-loader.js +50 -0
- package/loaders/judgeme-review-aggregate-loader.js.map +7 -0
- package/loaders/judgeme-reviews-loader.d.ts +14 -0
- package/loaders/judgeme-reviews-loader.js +65 -0
- package/loaders/judgeme-reviews-loader.js.map +7 -0
- package/loaders/loader-keys.d.ts +2 -0
- package/loaders/loader-keys.js +2 -0
- package/loaders/loader-keys.js.map +2 -2
- package/loaders/loader-schemas.d.ts +30 -0
- package/loaders/loader-schemas.js +22 -0
- package/loaders/loader-schemas.js.map +2 -2
- package/loaders/rebuy-recommendations-loader.d.ts +1 -0
- package/next/config.d.ts +18 -0
- package/next/config.js.map +2 -2
- package/package.json +12 -4
- package/providers/replo-provider.d.ts +19 -0
- 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
|
|
5
|
-
"mappings": ";AAWA,SAAS,eAAe;AAExB,SAAS,4BAA4B;AACrC,SAAS,2BAA2B;
|
|
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
|
}
|
package/gateways/canopy-api.d.ts
CHANGED
|
@@ -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;
|
package/gateways/canopy-api.js
CHANGED
|
@@ -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": "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",
|
|
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
|
}
|
package/lib/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sdk",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-09-
|
|
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
|
+
}
|
package/loaders/loader-keys.d.ts
CHANGED
|
@@ -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";
|
package/loaders/loader-keys.js
CHANGED
|
@@ -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,
|