@replohq/sdk 2.9.0 → 2.10.1
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/json.mjs +0 -1
- package/_vendor/replo-utils/lib/misc.mjs +0 -1
- package/_vendor/replo-utils/lib/object.mjs +0 -1
- package/_vendor/replo-utils/lib/url.d.ts +10 -3
- package/_vendor/schemas/contentCollection.mjs +0 -1
- package/_vendor/schemas/contentEntry.mjs +0 -1
- package/_vendor/schemas/contentManifest.mjs +0 -1
- package/_vendor/schemas/loaderKeys.mjs +0 -2
- package/_vendor/schemas/moneyFormat.mjs +42 -1853
- package/_vendor/schemas/routing/locale.mjs +0 -1
- package/_vendor/schemas/routing/localeNegotiation.mjs +0 -1
- package/_vendor/schemas/routing/match.mjs +0 -1
- package/_vendor/schemas/routing/rules.mjs +0 -1
- package/lib/buildMetadata.js +3 -3
- package/loaders/collection-loader.d.ts +4 -1
- package/loaders/collection-loader.js +5 -9
- package/loaders/collection-products-loader.d.ts +4 -1
- package/loaders/collection-products-loader.js +5 -9
- package/loaders/contentful-entries-loader.d.ts +4 -1
- package/loaders/contentful-entries-loader.js +2 -6
- package/loaders/contentful-entry-loader.d.ts +4 -1
- package/loaders/contentful-entry-loader.js +2 -6
- package/loaders/invoke-loader-action.d.ts +2 -1
- package/loaders/invoke-loader-action.js +2 -4
- package/loaders/invoke-loader-server.js +8 -4
- package/loaders/invoke-loader.d.ts +11 -3
- package/loaders/invoke-loader.js +6 -5
- package/loaders/judgeme-review-aggregate-loader.d.ts +4 -1
- package/loaders/judgeme-review-aggregate-loader.js +5 -11
- package/loaders/judgeme-reviews-loader.d.ts +4 -1
- package/loaders/judgeme-reviews-loader.js +2 -6
- package/loaders/junip-review-aggregate-loader.d.ts +4 -1
- package/loaders/junip-review-aggregate-loader.js +5 -11
- package/loaders/junip-reviews-loader.d.ts +4 -1
- package/loaders/junip-reviews-loader.js +2 -6
- package/loaders/kaching-bundles-loader.d.ts +4 -1
- package/loaders/kaching-bundles-loader.js +5 -6
- package/loaders/levanta-product-loader.d.ts +4 -1
- package/loaders/levanta-product-loader.js +5 -9
- package/loaders/loader-output.d.ts +28 -0
- package/loaders/loader-output.js +122 -0
- package/loaders/loader-outputs.d.ts +55 -0
- package/loaders/loader-outputs.js +0 -0
- package/loaders/loox-review-aggregate-loader.d.ts +4 -1
- package/loaders/loox-review-aggregate-loader.js +5 -11
- package/loaders/loox-reviews-loader.d.ts +4 -1
- package/loaders/loox-reviews-loader.js +5 -9
- package/loaders/loyaltylion-earning-rules-loader.js +5 -11
- package/loaders/loyaltylion-points-for-price-loader.js +5 -11
- package/loaders/loyaltylion-rewards-loader.js +5 -9
- package/loaders/loyaltylion-tiers-loader.js +5 -9
- package/loaders/metaobject-loader.d.ts +4 -1
- package/loaders/metaobject-loader.js +2 -6
- package/loaders/metaobjects-loader.d.ts +4 -1
- package/loaders/metaobjects-loader.js +2 -6
- package/loaders/okendo-review-aggregate-loader.d.ts +4 -1
- package/loaders/okendo-review-aggregate-loader.js +5 -9
- package/loaders/okendo-reviews-loader.d.ts +4 -1
- package/loaders/okendo-reviews-loader.js +2 -6
- package/loaders/product-loader.d.ts +4 -1
- package/loaders/product-loader.js +2 -6
- package/loaders/rebuy-recommendations-loader.d.ts +4 -1
- package/loaders/rebuy-recommendations-loader.js +5 -9
- package/loaders/reviews-io-review-aggregate-loader.d.ts +4 -1
- package/loaders/reviews-io-review-aggregate-loader.js +2 -8
- package/loaders/reviews-io-reviews-loader.d.ts +4 -1
- package/loaders/reviews-io-reviews-loader.js +2 -8
- package/loaders/shopify-integration-details-loader.d.ts +4 -1
- package/loaders/shopify-integration-details-loader.js +5 -11
- package/loaders/smile-earning-rules-loader.d.ts +4 -1
- package/loaders/smile-earning-rules-loader.js +5 -9
- package/loaders/smile-rewards-loader.d.ts +4 -1
- package/loaders/smile-rewards-loader.js +5 -9
- package/loaders/smile-vip-tiers-loader.d.ts +4 -1
- package/loaders/smile-vip-tiers-loader.js +5 -9
- package/loaders/statsig-experiment-loader.d.ts +4 -1
- package/loaders/statsig-experiment-loader.js +2 -6
- package/loaders/yotpo-review-aggregate-loader.d.ts +4 -1
- package/loaders/yotpo-review-aggregate-loader.js +5 -11
- package/loaders/yotpo-reviews-loader.d.ts +4 -1
- package/loaders/yotpo-reviews-loader.js +2 -6
- package/package.json +4 -4
- package/size-budget/fixture/app/fixture-pdp/page.d.ts +1 -1
- package/size-budget/fixture/app/fixture-pdp/product-details.d.ts +1 -0
- package/chunk-UJCSKKID.mjs +0 -23
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { getParamSegment, getRouteParams, hasRouteParam, matchSource } from '../../../chunk-VPEVERWE.mjs';
|
|
2
2
|
import { ReploError } from '../../../chunk-AE5H7AJS.mjs';
|
|
3
|
-
import '../../../chunk-UJCSKKID.mjs';
|
|
4
3
|
import * as z from 'zod';
|
|
5
4
|
|
|
6
5
|
function parseUrl(value, base) {
|
package/lib/buildMetadata.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export const BUILD_METADATA = {
|
|
2
2
|
"packageName": "@replohq/sdk",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.10.1",
|
|
4
4
|
"branch": "HEAD",
|
|
5
|
-
"commit": "
|
|
6
|
-
"builtAt": "2026-10-
|
|
5
|
+
"commit": "65e727be083f9c9b51cc9aa247d471777050f32c",
|
|
6
|
+
"builtAt": "2026-10-26T23:28:14.346Z"
|
|
7
7
|
};
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { CollectionLoaderArgs } from "./collection-loader-args";
|
|
3
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
4
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
3
5
|
import type { FullCollection } from "./loader-schemas";
|
|
6
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["SHOPIFY_COLLECTION"];
|
|
4
7
|
type CollectionLoaderProps = CollectionLoaderArgs & {
|
|
5
|
-
loaderKey:
|
|
8
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
6
9
|
children: (data: FullCollection) => ReactNode;
|
|
7
10
|
fallback?: ReactNode;
|
|
8
11
|
};
|
|
@@ -13,15 +13,11 @@ function CollectionLoaderInner({
|
|
|
13
13
|
}) {
|
|
14
14
|
const queryResult = useSuspenseQuery({
|
|
15
15
|
queryKey: [loaderKey, args],
|
|
16
|
-
queryFn:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}),
|
|
22
|
-
import("./loader-schemas")
|
|
23
|
-
]);
|
|
24
|
-
return schemas.fullCollectionSchema.parse(loadedData);
|
|
16
|
+
queryFn: () => {
|
|
17
|
+
return invokeLoader({
|
|
18
|
+
loaderKey,
|
|
19
|
+
args
|
|
20
|
+
});
|
|
25
21
|
}
|
|
26
22
|
});
|
|
27
23
|
useReportPreviewPlaceholderRender(queryResult.data);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { CollectionProductsLoaderArgs } from "./collection-products-loader-args";
|
|
3
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
4
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
3
5
|
import type { PaginatedProducts } from "./loader-schemas";
|
|
6
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["SHOPIFY_COLLECTION_PRODUCTS"];
|
|
4
7
|
type CollectionProductsLoaderProps = CollectionProductsLoaderArgs & {
|
|
5
|
-
loaderKey:
|
|
8
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
6
9
|
children: (data: PaginatedProducts) => ReactNode;
|
|
7
10
|
fallback?: ReactNode;
|
|
8
11
|
};
|
|
@@ -13,15 +13,11 @@ function CollectionProductsLoaderInner({
|
|
|
13
13
|
}) {
|
|
14
14
|
const queryResult = useSuspenseQuery({
|
|
15
15
|
queryKey: [loaderKey, args],
|
|
16
|
-
queryFn:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
}),
|
|
22
|
-
import("./loader-schemas")
|
|
23
|
-
]);
|
|
24
|
-
return schemas.paginatedProductsSchema.parse(loadedData);
|
|
16
|
+
queryFn: () => {
|
|
17
|
+
return invokeLoader({
|
|
18
|
+
loaderKey,
|
|
19
|
+
args
|
|
20
|
+
});
|
|
25
21
|
}
|
|
26
22
|
});
|
|
27
23
|
useReportPreviewPlaceholderRender(queryResult.data);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
3
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
2
4
|
import type { ContentfulEntriesLoaderOutput } from "./loader-schemas";
|
|
5
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["CONTENTFUL_ENTRIES"];
|
|
3
6
|
type ContentfulEntriesLoaderProps = {
|
|
4
|
-
loaderKey:
|
|
7
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
5
8
|
spaceId: string;
|
|
6
9
|
contentType: string;
|
|
7
10
|
environmentId?: string;
|
|
@@ -32,12 +32,8 @@ function ContentfulEntriesLoaderInner({
|
|
|
32
32
|
}) {
|
|
33
33
|
const { data } = useSuspenseQuery({
|
|
34
34
|
queryKey: [loaderKey, args],
|
|
35
|
-
queryFn:
|
|
36
|
-
|
|
37
|
-
invokeLoader({ loaderKey, args }),
|
|
38
|
-
import("./loader-schemas")
|
|
39
|
-
]);
|
|
40
|
-
return schemas.contentfulEntriesLoaderOutputSchema.parse(loadedData);
|
|
35
|
+
queryFn: () => {
|
|
36
|
+
return invokeLoader({ loaderKey, args });
|
|
41
37
|
}
|
|
42
38
|
});
|
|
43
39
|
return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
3
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
2
4
|
import type { ContentfulEntryLoaderOutput } from "./loader-schemas";
|
|
5
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["CONTENTFUL_ENTRY"];
|
|
3
6
|
type ContentfulEntryLoaderProps = {
|
|
4
|
-
loaderKey:
|
|
7
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
5
8
|
spaceId: string;
|
|
6
9
|
entryId: string;
|
|
7
10
|
environmentId?: string;
|
|
@@ -26,12 +26,8 @@ function ContentfulEntryLoaderInner({
|
|
|
26
26
|
}) {
|
|
27
27
|
const { data } = useSuspenseQuery({
|
|
28
28
|
queryKey: [loaderKey, args],
|
|
29
|
-
queryFn:
|
|
30
|
-
|
|
31
|
-
invokeLoader({ loaderKey, args }),
|
|
32
|
-
import("./loader-schemas")
|
|
33
|
-
]);
|
|
34
|
-
return schemas.contentfulEntryLoaderOutputSchema.parse(loadedData);
|
|
29
|
+
queryFn: () => {
|
|
30
|
+
return invokeLoader({ loaderKey, args });
|
|
35
31
|
}
|
|
36
32
|
});
|
|
37
33
|
return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
* Called from client-side loader components when React Query needs to
|
|
4
4
|
* fetch data in the browser (e.g., client-side navigation, cache miss).
|
|
5
5
|
* This runs on the server — Next.js handles the client/server boundary
|
|
6
|
-
* automatically.
|
|
6
|
+
* automatically. The output is validated here, before it crosses to the
|
|
7
|
+
* browser.
|
|
7
8
|
*/
|
|
8
9
|
export declare function invokeLoaderAction({ loaderKey, args, }: {
|
|
9
10
|
loaderKey: string;
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"use server";
|
|
2
|
-
import {
|
|
2
|
+
import { fetchValidatedLoaderOutput } from "./loader-output";
|
|
3
3
|
async function invokeLoaderAction({
|
|
4
4
|
loaderKey,
|
|
5
5
|
args
|
|
6
6
|
}) {
|
|
7
|
-
|
|
8
|
-
const response = await gateway.invokeLoader({ loaderKey, args });
|
|
9
|
-
return response.data;
|
|
7
|
+
return fetchValidatedLoaderOutput({ loaderKey, args });
|
|
10
8
|
}
|
|
11
9
|
export {
|
|
12
10
|
invokeLoaderAction
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { unstable_cache } from "next/cache";
|
|
2
2
|
import {
|
|
3
3
|
CanopyApiGatewayError,
|
|
4
|
-
getCanopyApiGateway,
|
|
5
4
|
shouldIncludeInactiveProducts
|
|
6
5
|
} from "../gateways/canopy-api";
|
|
6
|
+
import { fetchValidatedLoaderOutput, LoaderOutputError } from "./loader-output";
|
|
7
7
|
import { resolvePreviewPlaceholderData } from "./preview-placeholder";
|
|
8
8
|
const DEFAULT_REVALIDATE_SECONDS = 60;
|
|
9
9
|
class LoaderDataNotFoundError extends CanopyApiGatewayError {
|
|
@@ -46,11 +46,15 @@ async function invokeLoaderServer({
|
|
|
46
46
|
});
|
|
47
47
|
const cachedInvocation = await unstable_cache(
|
|
48
48
|
async () => {
|
|
49
|
-
const gateway = await getCanopyApiGateway();
|
|
50
49
|
try {
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
return {
|
|
51
|
+
kind: "data",
|
|
52
|
+
data: await fetchValidatedLoaderOutput({ loaderKey, args })
|
|
53
|
+
};
|
|
53
54
|
} catch (error) {
|
|
55
|
+
if (error instanceof LoaderOutputError) {
|
|
56
|
+
return { kind: "data", data: error.unvalidatedOutput };
|
|
57
|
+
}
|
|
54
58
|
if (error instanceof CanopyApiGatewayError && error.statusCode === 404) {
|
|
55
59
|
return { kind: "notFound" };
|
|
56
60
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { DataLoaderKey } from "./loader-keys";
|
|
2
|
+
import type { LoaderKeyProp, LoaderOutput } from "./loader-outputs";
|
|
1
3
|
import { CanopyError } from "../lib/canopy-error";
|
|
2
4
|
/** Scoped error for loader invocation failures. */
|
|
3
5
|
export declare class LoaderInvokeError extends CanopyError {
|
|
@@ -7,6 +9,12 @@ export declare class LoaderInvokeError extends CanopyError {
|
|
|
7
9
|
* In the browser, dynamically imports a server action so React 19 never sees
|
|
8
10
|
* the "use server" reference during render.
|
|
9
11
|
*
|
|
12
|
+
* `Key` names the loader the caller expects, and the result is typed from it.
|
|
13
|
+
* It is not checked here: `loaderKey` can be any string a site passed to a
|
|
14
|
+
* component, and the server validates the output against the schema
|
|
15
|
+
* registered for the key actually sent (`loader-output.ts`), so the browser
|
|
16
|
+
* never downloads Zod or the schemas. Callers pass `Key` explicitly.
|
|
17
|
+
*
|
|
10
18
|
* Next defines `process.env.NEXT_RUNTIME` as "" in browser bundles, so the
|
|
11
19
|
* bundler drops the server branch and never emits the gateway, its env
|
|
12
20
|
* handling, or its Zod schemas as a client chunk. `typeof window` is not
|
|
@@ -15,7 +23,7 @@ export declare class LoaderInvokeError extends CanopyError {
|
|
|
15
23
|
* In an editor iframe previewing an unfilled dynamic route, returns sample
|
|
16
24
|
* data instead of fetching — see `preview-placeholder.ts`.
|
|
17
25
|
*/
|
|
18
|
-
export declare function invokeLoader
|
|
19
|
-
loaderKey:
|
|
26
|
+
export declare function invokeLoader<Key extends DataLoaderKey>(params: {
|
|
27
|
+
loaderKey: LoaderKeyProp<Key>;
|
|
20
28
|
args: Record<string, unknown>;
|
|
21
|
-
}): Promise<
|
|
29
|
+
}): Promise<LoaderOutput<Key>>;
|
package/loaders/invoke-loader.js
CHANGED
|
@@ -2,7 +2,7 @@ import { CanopyError } from "../lib/canopy-error";
|
|
|
2
2
|
import { resolvePreviewPlaceholderData } from "./preview-placeholder";
|
|
3
3
|
class LoaderInvokeError extends CanopyError {
|
|
4
4
|
}
|
|
5
|
-
async function
|
|
5
|
+
async function invokeUntypedLoader({
|
|
6
6
|
loaderKey,
|
|
7
7
|
args
|
|
8
8
|
}) {
|
|
@@ -12,10 +12,8 @@ async function invokeLoader({
|
|
|
12
12
|
}
|
|
13
13
|
try {
|
|
14
14
|
if (process.env.NEXT_RUNTIME) {
|
|
15
|
-
const {
|
|
16
|
-
|
|
17
|
-
const response = await gateway.invokeLoader({ loaderKey, args });
|
|
18
|
-
return response.data;
|
|
15
|
+
const { fetchValidatedLoaderOutput } = await import("./loader-output");
|
|
16
|
+
return await fetchValidatedLoaderOutput({ loaderKey, args });
|
|
19
17
|
}
|
|
20
18
|
const { invokeLoaderAction } = await import("./invoke-loader-action");
|
|
21
19
|
return await invokeLoaderAction({ loaderKey, args });
|
|
@@ -30,6 +28,9 @@ async function invokeLoader({
|
|
|
30
28
|
});
|
|
31
29
|
}
|
|
32
30
|
}
|
|
31
|
+
async function invokeLoader(params) {
|
|
32
|
+
return await invokeUntypedLoader(params);
|
|
33
|
+
}
|
|
33
34
|
export {
|
|
34
35
|
LoaderInvokeError,
|
|
35
36
|
invokeLoader
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { z } from "zod";
|
|
3
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
4
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
3
5
|
import type { judgemeProductReviewAggregateLoaderOutputSchema } from "./loader-schemas";
|
|
6
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["JUDGEME_PRODUCT_REVIEW_AGGREGATE"];
|
|
4
7
|
type JudgemeReviewAggregateData = z.infer<typeof judgemeProductReviewAggregateLoaderOutputSchema>;
|
|
5
8
|
type JudgemeReviewAggregateLoaderProps = {
|
|
6
|
-
loaderKey:
|
|
9
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
7
10
|
productId: string;
|
|
8
11
|
children: (data: JudgemeReviewAggregateData) => ReactNode;
|
|
9
12
|
fallback?: ReactNode;
|
|
@@ -11,17 +11,11 @@ function JudgemeReviewAggregateLoaderInner({
|
|
|
11
11
|
}) {
|
|
12
12
|
const { data } = useSuspenseQuery({
|
|
13
13
|
queryKey: [loaderKey, { productId }],
|
|
14
|
-
queryFn:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}),
|
|
20
|
-
import("./loader-schemas")
|
|
21
|
-
]);
|
|
22
|
-
return schemas.judgemeProductReviewAggregateLoaderOutputSchema.parse(
|
|
23
|
-
loadedData
|
|
24
|
-
);
|
|
14
|
+
queryFn: () => {
|
|
15
|
+
return invokeLoader({
|
|
16
|
+
loaderKey,
|
|
17
|
+
args: { productId }
|
|
18
|
+
});
|
|
25
19
|
}
|
|
26
20
|
});
|
|
27
21
|
return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { z } from "zod";
|
|
3
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
4
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
3
5
|
import type { judgemeProductReviewsLoaderOutputSchema } from "./loader-schemas";
|
|
6
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["JUDGEME_PRODUCT_REVIEWS"];
|
|
4
7
|
type JudgemeReviewsData = z.infer<typeof judgemeProductReviewsLoaderOutputSchema>;
|
|
5
8
|
type JudgemeReviewsLoaderProps = {
|
|
6
|
-
loaderKey:
|
|
9
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
7
10
|
productId: string;
|
|
8
11
|
perPage?: number;
|
|
9
12
|
page?: number;
|
|
@@ -25,12 +25,8 @@ function JudgemeReviewsLoaderInner({
|
|
|
25
25
|
const args = buildReviewsArgs({ productId, perPage, page });
|
|
26
26
|
const { data } = useSuspenseQuery({
|
|
27
27
|
queryKey: [loaderKey, args],
|
|
28
|
-
queryFn:
|
|
29
|
-
|
|
30
|
-
invokeLoader({ loaderKey, args }),
|
|
31
|
-
import("./loader-schemas")
|
|
32
|
-
]);
|
|
33
|
-
return schemas.judgemeProductReviewsLoaderOutputSchema.parse(loadedData);
|
|
28
|
+
queryFn: () => {
|
|
29
|
+
return invokeLoader({ loaderKey, args });
|
|
34
30
|
}
|
|
35
31
|
});
|
|
36
32
|
return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { z } from "zod";
|
|
3
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
4
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
3
5
|
import type { junipProductReviewAggregateLoaderOutputSchema } from "./loader-schemas";
|
|
6
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["JUNIP_PRODUCT_REVIEW_AGGREGATE"];
|
|
4
7
|
type JunipReviewAggregateData = z.infer<typeof junipProductReviewAggregateLoaderOutputSchema>;
|
|
5
8
|
type JunipReviewAggregateLoaderProps = {
|
|
6
|
-
loaderKey:
|
|
9
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
7
10
|
productId: string;
|
|
8
11
|
children: (data: JunipReviewAggregateData) => ReactNode;
|
|
9
12
|
fallback?: ReactNode;
|
|
@@ -11,17 +11,11 @@ function JunipReviewAggregateLoaderInner({
|
|
|
11
11
|
}) {
|
|
12
12
|
const { data } = useSuspenseQuery({
|
|
13
13
|
queryKey: [loaderKey, { productId }],
|
|
14
|
-
queryFn:
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}),
|
|
20
|
-
import("./loader-schemas")
|
|
21
|
-
]);
|
|
22
|
-
return schemas.junipProductReviewAggregateLoaderOutputSchema.parse(
|
|
23
|
-
loadedData
|
|
24
|
-
);
|
|
14
|
+
queryFn: () => {
|
|
15
|
+
return invokeLoader({
|
|
16
|
+
loaderKey,
|
|
17
|
+
args: { productId }
|
|
18
|
+
});
|
|
25
19
|
}
|
|
26
20
|
});
|
|
27
21
|
return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { z } from "zod";
|
|
3
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
4
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
3
5
|
import type { junipProductReviewsLoaderOutputSchema } from "./loader-schemas";
|
|
6
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["JUNIP_PRODUCT_REVIEWS"];
|
|
4
7
|
type JunipReviewsData = z.infer<typeof junipProductReviewsLoaderOutputSchema>;
|
|
5
8
|
type JunipReviewsLoaderProps = {
|
|
6
|
-
loaderKey:
|
|
9
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
7
10
|
productId: string;
|
|
8
11
|
perPage?: number;
|
|
9
12
|
children: (data: JunipReviewsData) => ReactNode;
|
|
@@ -19,12 +19,8 @@ function JunipReviewsLoaderInner({
|
|
|
19
19
|
const args = buildReviewsArgs({ productId, perPage });
|
|
20
20
|
const { data } = useSuspenseQuery({
|
|
21
21
|
queryKey: [loaderKey, args],
|
|
22
|
-
queryFn:
|
|
23
|
-
|
|
24
|
-
invokeLoader({ loaderKey, args }),
|
|
25
|
-
import("./loader-schemas")
|
|
26
|
-
]);
|
|
27
|
-
return schemas.junipProductReviewsLoaderOutputSchema.parse(loadedData);
|
|
22
|
+
queryFn: () => {
|
|
23
|
+
return invokeLoader({ loaderKey, args });
|
|
28
24
|
}
|
|
29
25
|
});
|
|
30
26
|
return /* @__PURE__ */ jsx(Fragment, { children: children(data) });
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { AddToCartItem } from "../cart/hooks/use-add-to-cart";
|
|
3
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
4
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
5
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["SHOPIFY_KACHING_BUNDLES"];
|
|
3
6
|
type KachingBundlesLoaderProps = {
|
|
4
|
-
loaderKey:
|
|
7
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
5
8
|
productId: string;
|
|
6
9
|
dealBlockId?: string;
|
|
7
10
|
country?: string;
|
|
@@ -15,12 +15,11 @@ function KachingBundlesLoaderInner({
|
|
|
15
15
|
const [lines, setLines] = useState([]);
|
|
16
16
|
const { data } = useSuspenseQuery({
|
|
17
17
|
queryKey: [loaderKey, args],
|
|
18
|
-
queryFn:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return schemas.shopifyKachingBundlesLoaderOutputSchema.parse(loadedData);
|
|
18
|
+
queryFn: () => {
|
|
19
|
+
return invokeLoader({
|
|
20
|
+
loaderKey,
|
|
21
|
+
args
|
|
22
|
+
});
|
|
24
23
|
}
|
|
25
24
|
});
|
|
26
25
|
if (data.bundles === null) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import type { DATA_LOADER_KEYS } from "./loader-keys";
|
|
3
|
+
import type { LoaderKeyProp } from "./loader-outputs";
|
|
2
4
|
import type { LevantaProductFull } from "./loader-schemas";
|
|
5
|
+
type LoaderKey = (typeof DATA_LOADER_KEYS)["LEVANTA_PRODUCT"];
|
|
3
6
|
type LevantaProductLoaderProps = {
|
|
4
|
-
loaderKey:
|
|
7
|
+
loaderKey: LoaderKeyProp<LoaderKey>;
|
|
5
8
|
asin: string;
|
|
6
9
|
marketplace?: string;
|
|
7
10
|
children: (data: LevantaProductFull) => ReactNode;
|
|
@@ -12,15 +12,11 @@ function LevantaProductLoaderInner({
|
|
|
12
12
|
}) {
|
|
13
13
|
const queryResult = useSuspenseQuery({
|
|
14
14
|
queryKey: [loaderKey, { asin, marketplace }],
|
|
15
|
-
queryFn:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}),
|
|
21
|
-
import("./loader-schemas")
|
|
22
|
-
]);
|
|
23
|
-
return schemas.levantaProductFullSchema.parse(loadedData);
|
|
15
|
+
queryFn: () => {
|
|
16
|
+
return invokeLoader({
|
|
17
|
+
loaderKey,
|
|
18
|
+
args: { asin, marketplace }
|
|
19
|
+
});
|
|
24
20
|
}
|
|
25
21
|
});
|
|
26
22
|
return /* @__PURE__ */ jsx(Fragment, { children: children(queryResult.data) });
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { DataLoaderKey } from "./loader-keys";
|
|
2
|
+
import * as z from "zod";
|
|
3
|
+
import { CanopyError } from "../lib/canopy-error";
|
|
4
|
+
/**
|
|
5
|
+
* A loader returned data that does not match its output contract. Carries the
|
|
6
|
+
* unvalidated output so a caller that fails soft can still serve it.
|
|
7
|
+
*/
|
|
8
|
+
export declare class LoaderOutputError extends CanopyError {
|
|
9
|
+
readonly unvalidatedOutput: unknown;
|
|
10
|
+
constructor({ loaderKey, unvalidatedOutput, zodError, }: {
|
|
11
|
+
loaderKey: DataLoaderKey;
|
|
12
|
+
unvalidatedOutput: unknown;
|
|
13
|
+
zodError: z.ZodError;
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Invokes a loader through the canopy-api gateway and validates its output
|
|
18
|
+
* against the loader's contract. The parsed value drops fields the contract
|
|
19
|
+
* does not declare. Output of a loader key the SDK has no contract for passes
|
|
20
|
+
* through unchanged.
|
|
21
|
+
*
|
|
22
|
+
* Malformed output is logged with the loader key and the failing paths, then
|
|
23
|
+
* rejected with a `LoaderOutputError`; callers decide whether to fail soft.
|
|
24
|
+
*/
|
|
25
|
+
export declare function fetchValidatedLoaderOutput({ loaderKey, args, }: {
|
|
26
|
+
loaderKey: string;
|
|
27
|
+
args: Record<string, unknown>;
|
|
28
|
+
}): Promise<unknown>;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
import { getCanopyApiGateway } from "../gateways/canopy-api";
|
|
3
|
+
import { CanopyError } from "../lib/canopy-error";
|
|
4
|
+
import {
|
|
5
|
+
okendoReviewAggregateOutputSchema,
|
|
6
|
+
okendoReviewsOutputSchema,
|
|
7
|
+
rebuyRecommendationsOutputSchema,
|
|
8
|
+
smileEarningRulesOutputSchema,
|
|
9
|
+
smileRewardsOutputSchema,
|
|
10
|
+
smileVipTiersOutputSchema
|
|
11
|
+
} from "./integration-loader-schemas";
|
|
12
|
+
import { DATA_LOADER_KEYS } from "./loader-keys";
|
|
13
|
+
import {
|
|
14
|
+
contentfulEntriesLoaderOutputSchema,
|
|
15
|
+
contentfulEntryLoaderOutputSchema,
|
|
16
|
+
fullCollectionSchema,
|
|
17
|
+
fullProductSchema,
|
|
18
|
+
judgemeProductReviewAggregateLoaderOutputSchema,
|
|
19
|
+
judgemeProductReviewsLoaderOutputSchema,
|
|
20
|
+
junipProductReviewAggregateLoaderOutputSchema,
|
|
21
|
+
junipProductReviewsLoaderOutputSchema,
|
|
22
|
+
levantaProductFullSchema,
|
|
23
|
+
looxProductReviewAggregateLoaderOutputSchema,
|
|
24
|
+
looxProductReviewsLoaderOutputSchema,
|
|
25
|
+
loyaltyLionEarningRulesLoaderOutputSchema,
|
|
26
|
+
loyaltyLionPointsForPriceLoaderOutputSchema,
|
|
27
|
+
loyaltyLionRewardsLoaderOutputSchema,
|
|
28
|
+
loyaltyLionTiersLoaderOutputSchema,
|
|
29
|
+
paginatedProductsSchema,
|
|
30
|
+
reviewsIoProductReviewAggregateLoaderOutputSchema,
|
|
31
|
+
reviewsIoProductReviewsLoaderOutputSchema,
|
|
32
|
+
shopifyIntegrationDetailsLoaderOutputSchema,
|
|
33
|
+
shopifyKachingBundlesLoaderOutputSchema,
|
|
34
|
+
shopifyMetaobjectLoaderOutputSchema,
|
|
35
|
+
shopifyMetaobjectsLoaderOutputSchema,
|
|
36
|
+
statsigExperimentLoaderOutputSchema,
|
|
37
|
+
yotpoProductReviewAggregateLoaderOutputSchema,
|
|
38
|
+
yotpoProductReviewsLoaderOutputSchema
|
|
39
|
+
} from "./loader-schemas";
|
|
40
|
+
const LOADER_OUTPUT_SCHEMAS = {
|
|
41
|
+
[DATA_LOADER_KEYS.SHOPIFY_INTEGRATION_DETAILS]: shopifyIntegrationDetailsLoaderOutputSchema,
|
|
42
|
+
[DATA_LOADER_KEYS.SHOPIFY_PRODUCT]: fullProductSchema,
|
|
43
|
+
[DATA_LOADER_KEYS.SHOPIFY_COLLECTION]: fullCollectionSchema,
|
|
44
|
+
[DATA_LOADER_KEYS.SHOPIFY_COLLECTION_PRODUCTS]: paginatedProductsSchema,
|
|
45
|
+
[DATA_LOADER_KEYS.SHOPIFY_METAOBJECT]: shopifyMetaobjectLoaderOutputSchema,
|
|
46
|
+
[DATA_LOADER_KEYS.SHOPIFY_METAOBJECTS]: shopifyMetaobjectsLoaderOutputSchema,
|
|
47
|
+
[DATA_LOADER_KEYS.SHOPIFY_KACHING_BUNDLES]: shopifyKachingBundlesLoaderOutputSchema,
|
|
48
|
+
[DATA_LOADER_KEYS.REPLO_PRODUCT]: fullProductSchema,
|
|
49
|
+
[DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEWS]: okendoReviewsOutputSchema,
|
|
50
|
+
[DATA_LOADER_KEYS.OKENDO_PRODUCT_REVIEW_AGGREGATE]: okendoReviewAggregateOutputSchema,
|
|
51
|
+
[DATA_LOADER_KEYS.REBUY_RECOMMENDATIONS]: rebuyRecommendationsOutputSchema,
|
|
52
|
+
[DATA_LOADER_KEYS.LEVANTA_PRODUCT]: levantaProductFullSchema,
|
|
53
|
+
[DATA_LOADER_KEYS.SMILE_IO_VIP_TIERS]: smileVipTiersOutputSchema,
|
|
54
|
+
[DATA_LOADER_KEYS.SMILE_IO_EARNING_RULES]: smileEarningRulesOutputSchema,
|
|
55
|
+
[DATA_LOADER_KEYS.SMILE_IO_REWARDS]: smileRewardsOutputSchema,
|
|
56
|
+
[DATA_LOADER_KEYS.LOYALTYLION_EARNING_RULES]: loyaltyLionEarningRulesLoaderOutputSchema,
|
|
57
|
+
[DATA_LOADER_KEYS.LOYALTYLION_REWARDS]: loyaltyLionRewardsLoaderOutputSchema,
|
|
58
|
+
[DATA_LOADER_KEYS.LOYALTYLION_TIERS]: loyaltyLionTiersLoaderOutputSchema,
|
|
59
|
+
[DATA_LOADER_KEYS.LOYALTYLION_POINTS_FOR_PRICE]: loyaltyLionPointsForPriceLoaderOutputSchema,
|
|
60
|
+
[DATA_LOADER_KEYS.YOTPO_PRODUCT_REVIEWS]: yotpoProductReviewsLoaderOutputSchema,
|
|
61
|
+
[DATA_LOADER_KEYS.YOTPO_PRODUCT_REVIEW_AGGREGATE]: yotpoProductReviewAggregateLoaderOutputSchema,
|
|
62
|
+
[DATA_LOADER_KEYS.REVIEWS_IO_PRODUCT_REVIEWS]: reviewsIoProductReviewsLoaderOutputSchema,
|
|
63
|
+
[DATA_LOADER_KEYS.REVIEWS_IO_PRODUCT_REVIEW_AGGREGATE]: reviewsIoProductReviewAggregateLoaderOutputSchema,
|
|
64
|
+
[DATA_LOADER_KEYS.LOOX_PRODUCT_REVIEWS]: looxProductReviewsLoaderOutputSchema,
|
|
65
|
+
[DATA_LOADER_KEYS.LOOX_PRODUCT_REVIEW_AGGREGATE]: looxProductReviewAggregateLoaderOutputSchema,
|
|
66
|
+
[DATA_LOADER_KEYS.JUDGEME_PRODUCT_REVIEWS]: judgemeProductReviewsLoaderOutputSchema,
|
|
67
|
+
[DATA_LOADER_KEYS.JUDGEME_PRODUCT_REVIEW_AGGREGATE]: judgemeProductReviewAggregateLoaderOutputSchema,
|
|
68
|
+
[DATA_LOADER_KEYS.JUNIP_PRODUCT_REVIEWS]: junipProductReviewsLoaderOutputSchema,
|
|
69
|
+
[DATA_LOADER_KEYS.JUNIP_PRODUCT_REVIEW_AGGREGATE]: junipProductReviewAggregateLoaderOutputSchema,
|
|
70
|
+
[DATA_LOADER_KEYS.STATSIG_EXPERIMENT]: statsigExperimentLoaderOutputSchema,
|
|
71
|
+
[DATA_LOADER_KEYS.CONTENTFUL_ENTRY]: contentfulEntryLoaderOutputSchema,
|
|
72
|
+
[DATA_LOADER_KEYS.CONTENTFUL_ENTRIES]: contentfulEntriesLoaderOutputSchema
|
|
73
|
+
};
|
|
74
|
+
const dataLoaderKeySchema = z.enum(DATA_LOADER_KEYS);
|
|
75
|
+
class LoaderOutputError extends CanopyError {
|
|
76
|
+
unvalidatedOutput;
|
|
77
|
+
constructor({
|
|
78
|
+
loaderKey,
|
|
79
|
+
unvalidatedOutput,
|
|
80
|
+
zodError
|
|
81
|
+
}) {
|
|
82
|
+
super({
|
|
83
|
+
message: `Loader ${loaderKey} returned data that does not match its output schema: ${zodError.message}`,
|
|
84
|
+
cause: zodError,
|
|
85
|
+
additionalData: { loaderKey }
|
|
86
|
+
});
|
|
87
|
+
this.unvalidatedOutput = unvalidatedOutput;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
async function fetchValidatedLoaderOutput({
|
|
91
|
+
loaderKey,
|
|
92
|
+
args
|
|
93
|
+
}) {
|
|
94
|
+
const gateway = await getCanopyApiGateway();
|
|
95
|
+
const { data } = await gateway.invokeLoader({ loaderKey, args });
|
|
96
|
+
const knownKey = dataLoaderKeySchema.safeParse(loaderKey);
|
|
97
|
+
if (!knownKey.success) {
|
|
98
|
+
return data;
|
|
99
|
+
}
|
|
100
|
+
const result = LOADER_OUTPUT_SCHEMAS[knownKey.data].safeParse(data);
|
|
101
|
+
if (result.success) {
|
|
102
|
+
return result.data;
|
|
103
|
+
}
|
|
104
|
+
console.error(
|
|
105
|
+
`[Replo] Loader ${knownKey.data} returned data that does not match its output schema`,
|
|
106
|
+
{
|
|
107
|
+
loaderKey: knownKey.data,
|
|
108
|
+
issues: result.error.issues.map((issue) => {
|
|
109
|
+
return { path: issue.path.join("."), message: issue.message };
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
throw new LoaderOutputError({
|
|
114
|
+
loaderKey: knownKey.data,
|
|
115
|
+
unvalidatedOutput: data,
|
|
116
|
+
zodError: result.error
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
export {
|
|
120
|
+
LoaderOutputError,
|
|
121
|
+
fetchValidatedLoaderOutput
|
|
122
|
+
};
|