@decocms/apps-wake 7.66.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/package.json +58 -0
- package/src/actions/cart/addCoupon.ts +43 -0
- package/src/actions/cart/addItem.ts +10 -0
- package/src/actions/cart/addItems.ts +50 -0
- package/src/actions/cart/addKit.ts +53 -0
- package/src/actions/cart/partnerAssociate.ts +48 -0
- package/src/actions/cart/partnerDisassociate.ts +43 -0
- package/src/actions/cart/removeCoupon.ts +39 -0
- package/src/actions/cart/removeKit.ts +56 -0
- package/src/actions/cart/updateItemQuantity.ts +69 -0
- package/src/actions/newsletter/register.ts +32 -0
- package/src/actions/notifyme.ts +28 -0
- package/src/actions/review/create.ts +30 -0
- package/src/actions/shippingSimulation.ts +60 -0
- package/src/actions/submmitForm.ts +25 -0
- package/src/actions/wishlist/addProduct.ts +43 -0
- package/src/actions/wishlist/removeProduct.ts +45 -0
- package/src/client.ts +123 -0
- package/src/commerceLoaders.ts +137 -0
- package/src/handlers/sitemap.ts +61 -0
- package/src/index.ts +58 -0
- package/src/loaders/cart.ts +46 -0
- package/src/loaders/partners.ts +58 -0
- package/src/loaders/productDetailsPage.ts +174 -0
- package/src/loaders/productList.ts +262 -0
- package/src/loaders/productListingPage.ts +333 -0
- package/src/loaders/proxy.ts +79 -0
- package/src/loaders/recommendations.ts +84 -0
- package/src/loaders/shop.ts +37 -0
- package/src/loaders/suggestion.ts +67 -0
- package/src/loaders/user.ts +48 -0
- package/src/loaders/wishlist.ts +40 -0
- package/src/manifest.gen.ts +69 -0
- package/src/mod.ts +88 -0
- package/src/registry.ts +9 -0
- package/src/utils/authError.ts +107 -0
- package/src/utils/authenticate.ts +42 -0
- package/src/utils/capitalize.ts +9 -0
- package/src/utils/cart.ts +33 -0
- package/src/utils/client.ts +11 -0
- package/src/utils/cookies.ts +82 -0
- package/src/utils/getVariations.ts +47 -0
- package/src/utils/graphql/queries.ts +1363 -0
- package/src/utils/graphql/storefront.graphql.gen.ts +10233 -0
- package/src/utils/graphql.ts +88 -0
- package/src/utils/graphqlOperationName.ts +67 -0
- package/src/utils/httpError.ts +15 -0
- package/src/utils/instrumentedFetch.ts +46 -0
- package/src/utils/operationRouter.ts +51 -0
- package/src/utils/parseHeaders.ts +19 -0
- package/src/utils/partner.ts +34 -0
- package/src/utils/requestCtx.ts +41 -0
- package/src/utils/transform.ts +460 -0
- package/src/utils/types.ts +6 -0
- package/src/utils/user.ts +9 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { getWakeClient } from "../../client";
|
|
2
|
+
import authenticate from "../../utils/authenticate";
|
|
3
|
+
import { WishlistAddProduct } from "../../utils/graphql/queries";
|
|
4
|
+
import type {
|
|
5
|
+
ProductFragment,
|
|
6
|
+
WishlistAddProductMutation,
|
|
7
|
+
WishlistReducedProductFragment,
|
|
8
|
+
} from "../../utils/graphql/storefront.graphql.gen";
|
|
9
|
+
import { forwardedHeaders } from "../../utils/requestCtx";
|
|
10
|
+
|
|
11
|
+
export interface Props {
|
|
12
|
+
productId: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const action = async (props: Props): Promise<WishlistReducedProductFragment[] | null> => {
|
|
16
|
+
const storefront = getWakeClient();
|
|
17
|
+
const { productId } = props;
|
|
18
|
+
const customerAccessToken = await authenticate();
|
|
19
|
+
const headers = forwardedHeaders();
|
|
20
|
+
|
|
21
|
+
if (!customerAccessToken) return [];
|
|
22
|
+
|
|
23
|
+
const data = await storefront.query<WishlistAddProductMutation>(
|
|
24
|
+
WishlistAddProduct,
|
|
25
|
+
{ customerAccessToken, productId },
|
|
26
|
+
headers,
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
const products = data.wishlistAddProduct;
|
|
30
|
+
|
|
31
|
+
if (!Array.isArray(products)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return products
|
|
36
|
+
.filter((node): node is ProductFragment => Boolean(node))
|
|
37
|
+
.map(({ productId, productName }) => ({
|
|
38
|
+
productId,
|
|
39
|
+
productName,
|
|
40
|
+
}));
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default action;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { getWakeClient } from "../../client";
|
|
2
|
+
import authenticate from "../../utils/authenticate";
|
|
3
|
+
import { WishlistRemoveProduct } from "../../utils/graphql/queries";
|
|
4
|
+
import type {
|
|
5
|
+
ProductFragment,
|
|
6
|
+
WishlistReducedProductFragment,
|
|
7
|
+
WishlistRemoveProductMutation,
|
|
8
|
+
} from "../../utils/graphql/storefront.graphql.gen";
|
|
9
|
+
import { forwardedHeaders } from "../../utils/requestCtx";
|
|
10
|
+
|
|
11
|
+
export interface Props {
|
|
12
|
+
productId: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const action = async (props: Props): Promise<WishlistReducedProductFragment[] | null> => {
|
|
16
|
+
const storefront = getWakeClient();
|
|
17
|
+
const { productId } = props;
|
|
18
|
+
|
|
19
|
+
const headers = forwardedHeaders();
|
|
20
|
+
|
|
21
|
+
const customerAccessToken = await authenticate();
|
|
22
|
+
|
|
23
|
+
if (!customerAccessToken) return [];
|
|
24
|
+
|
|
25
|
+
const data = await storefront.query<WishlistRemoveProductMutation>(
|
|
26
|
+
WishlistRemoveProduct,
|
|
27
|
+
{ customerAccessToken, productId },
|
|
28
|
+
headers,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
const products = data.wishlistRemoveProduct;
|
|
32
|
+
|
|
33
|
+
if (!Array.isArray(products)) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return products
|
|
38
|
+
.filter((node): node is ProductFragment => Boolean(node))
|
|
39
|
+
.map(({ productId, productName }) => ({
|
|
40
|
+
productId,
|
|
41
|
+
productName,
|
|
42
|
+
}));
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default action;
|
package/src/client.ts
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { FetchFn } from "@decocms/blocks/sdk/fetchTimeout";
|
|
2
|
+
import { createGraphqlClient, type GraphQLClient } from "./utils/graphql";
|
|
3
|
+
|
|
4
|
+
export interface WakeConfig {
|
|
5
|
+
/** Wake account name, e.g. "erploja2". */
|
|
6
|
+
account: string;
|
|
7
|
+
/**
|
|
8
|
+
* Checkout/login domain, e.g. "https://secure.sprint55.com.br". Used only for
|
|
9
|
+
* the checkout REST API (login/cart) and proxy — NOT for the storefront
|
|
10
|
+
* GraphQL, which is a fixed multi-tenant endpoint resolved by the token.
|
|
11
|
+
*/
|
|
12
|
+
checkoutUrl: string;
|
|
13
|
+
/** Wake Storefront API token (sent as `TCS-Access-Token`). */
|
|
14
|
+
storefrontToken: string;
|
|
15
|
+
/**
|
|
16
|
+
* Storefront GraphQL endpoint. Defaults to Wake's canonical multi-tenant
|
|
17
|
+
* endpoint; override only for a custom storefront host.
|
|
18
|
+
*/
|
|
19
|
+
storefrontEndpoint?: string;
|
|
20
|
+
/** Wake Admin API token (Basic auth). Currently unused by the loaders/actions. */
|
|
21
|
+
token?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const STOREFRONT_ENDPOINT = "https://storefront-api.fbits.net/graphql";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Read an env var. Wake's tokens are provided ONLY via environment variables
|
|
28
|
+
* (no CMS secret / `resolveSecret`): `WAKE_TOKEN` (storefront) and, if ever
|
|
29
|
+
* needed, `WAKE_KEY` (admin). `process.env` is populated in Node and in
|
|
30
|
+
* Cloudflare Workers via `nodejs_compat` — the same source the framework's
|
|
31
|
+
* own `getEnvVar()` reads first.
|
|
32
|
+
*/
|
|
33
|
+
function readEnv(name: string): string | undefined {
|
|
34
|
+
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process
|
|
35
|
+
?.env;
|
|
36
|
+
const value = env?.[name];
|
|
37
|
+
return value && value.length > 0 ? value : undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Wake Storefront API token from `WAKE_TOKEN`. */
|
|
41
|
+
export function getWakeStorefrontTokenFromEnv(): string | undefined {
|
|
42
|
+
return readEnv("WAKE_TOKEN");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Wake Admin API token from `WAKE_KEY` (currently unused by loaders/actions). */
|
|
46
|
+
export function getWakeAdminTokenFromEnv(): string | undefined {
|
|
47
|
+
return readEnv("WAKE_KEY");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let _client: GraphQLClient | null = null;
|
|
51
|
+
let _config: WakeConfig | null = null;
|
|
52
|
+
let _fetch: FetchFn | undefined;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Override the fetch function used by the Wake GraphQL client. Use this to plug
|
|
56
|
+
* in an instrumented fetch for logging/tracing.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* import { createInstrumentedFetch } from "@decocms/blocks/sdk/instrumentedFetch";
|
|
61
|
+
* import { setWakeFetch } from "@decocms/apps-wake";
|
|
62
|
+
* setWakeFetch(createInstrumentedFetch("wake"));
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function setWakeFetch(fetchFn: FetchFn) {
|
|
66
|
+
_fetch = fetchFn;
|
|
67
|
+
if (_config) configureWake(_config);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function configureWake(config: WakeConfig) {
|
|
71
|
+
_config = config;
|
|
72
|
+
const endpoint = config.storefrontEndpoint || STOREFRONT_ENDPOINT;
|
|
73
|
+
_client = createGraphqlClient(
|
|
74
|
+
endpoint,
|
|
75
|
+
{
|
|
76
|
+
"TCS-Access-Token": config.storefrontToken,
|
|
77
|
+
},
|
|
78
|
+
_fetch,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function getWakeClient(): GraphQLClient {
|
|
83
|
+
if (!_client || !_config) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
"Wake not configured. Call configureWake() first or check deco-wake.json block.",
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return _client;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function getWakeConfig(): WakeConfig {
|
|
92
|
+
if (!_config) {
|
|
93
|
+
throw new Error("Wake not configured.");
|
|
94
|
+
}
|
|
95
|
+
return _config;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Base URL of the checkout REST API (`GET /api/Login/Get`, proxied paths). */
|
|
99
|
+
export function getCheckoutUrl(): string {
|
|
100
|
+
const config = getWakeConfig();
|
|
101
|
+
return config.checkoutUrl || `https://${config.account}.checkout.fbits.store`;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function getBaseUrl(): string {
|
|
105
|
+
return _config?.checkoutUrl || "";
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Convenience initializer used by the site setup: reads the Wake app block from
|
|
110
|
+
* the generated blocks map and configures the singleton.
|
|
111
|
+
*/
|
|
112
|
+
export function initWakeFromBlocks(blocks: Record<string, unknown>) {
|
|
113
|
+
const block = (blocks.wake ?? blocks["deco-wake"]) as Partial<WakeConfig> | undefined;
|
|
114
|
+
if (!block?.account) return;
|
|
115
|
+
configureWake({
|
|
116
|
+
account: block.account as string,
|
|
117
|
+
checkoutUrl: (block.checkoutUrl as string) ?? "",
|
|
118
|
+
// Tokens come from env only (WAKE_TOKEN / WAKE_KEY), never the CMS block.
|
|
119
|
+
storefrontToken: getWakeStorefrontTokenFromEnv() ?? "",
|
|
120
|
+
storefrontEndpoint: block.storefrontEndpoint as string | undefined,
|
|
121
|
+
token: getWakeAdminTokenFromEnv(),
|
|
122
|
+
});
|
|
123
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standard Wake commerce loader map factory for CMS block resolution.
|
|
3
|
+
*
|
|
4
|
+
* Wraps the cacheable catalog loaders with `createCachedLoader` (single-flight
|
|
5
|
+
* dedup + SWR + stale-if-error via the unified cache profiles), applies the
|
|
6
|
+
* PDP slug fallback and PLP page-URL resolution, and registers both `.ts` and
|
|
7
|
+
* `.ts`-less aliases.
|
|
8
|
+
*
|
|
9
|
+
* Sites call `createWakeCommerceLoaders()` and hand the result to
|
|
10
|
+
* `registerCommerceLoaders(...)` instead of wiring each loader (and its cache
|
|
11
|
+
* policy) by hand — mirroring `createVtexCommerceLoaders()`.
|
|
12
|
+
*
|
|
13
|
+
* User-specific loaders (cart/user/wishlist) are intentionally NOT included:
|
|
14
|
+
* they are per-session and must not be cached; the app manifest registers them
|
|
15
|
+
* uncached via `autoconfigApps`.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { createCachedLoader } from "@decocms/blocks/sdk/cachedLoader";
|
|
19
|
+
import type { CacheProfileName } from "@decocms/blocks/sdk/cacheHeaders";
|
|
20
|
+
import { RequestContext } from "@decocms/blocks/sdk/requestContext";
|
|
21
|
+
import partnersLoader from "./loaders/partners";
|
|
22
|
+
import productDetailsPage from "./loaders/productDetailsPage";
|
|
23
|
+
import productList from "./loaders/productList";
|
|
24
|
+
import productListingPage from "./loaders/productListingPage";
|
|
25
|
+
import recommendations from "./loaders/recommendations";
|
|
26
|
+
import shopLoader from "./loaders/shop";
|
|
27
|
+
import suggestion from "./loaders/suggestion";
|
|
28
|
+
|
|
29
|
+
export type CommerceLoaderFn = (props: any) => Promise<any>;
|
|
30
|
+
|
|
31
|
+
export interface WakeCommerceLoadersOptions {
|
|
32
|
+
/** Override cache profiles per loader type. */
|
|
33
|
+
cacheProfiles?: {
|
|
34
|
+
listing?: CacheProfileName;
|
|
35
|
+
product?: CacheProfileName;
|
|
36
|
+
search?: CacheProfileName;
|
|
37
|
+
static?: CacheProfileName;
|
|
38
|
+
};
|
|
39
|
+
/** Additional loaders to merge into the map (site-specific). */
|
|
40
|
+
extra?: Record<string, CommerceLoaderFn>;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const PAGE_URL_HEADER = "x-deco-page-url";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the real page URL for the productListingPage loader. Wake's PLP reads
|
|
47
|
+
* the URL from `props.pageHref`; the framework injects `__pageUrl`, but on CSR
|
|
48
|
+
* home navigations that is unreliable, so an explicit `x-deco-page-url` header
|
|
49
|
+
* (set by the site) wins, then `__pageUrl`, then the worker request URL.
|
|
50
|
+
*/
|
|
51
|
+
function resolvePageHref(props: any): string | undefined {
|
|
52
|
+
try {
|
|
53
|
+
const headerUrl = RequestContext.request.headers.get(PAGE_URL_HEADER);
|
|
54
|
+
if (headerUrl) return new URL(headerUrl, "http://localhost").href;
|
|
55
|
+
} catch {
|
|
56
|
+
// RequestContext may be unavailable in isolated calls.
|
|
57
|
+
}
|
|
58
|
+
if (props?.__pageUrl) {
|
|
59
|
+
try {
|
|
60
|
+
return new URL(props.__pageUrl, "http://localhost").href;
|
|
61
|
+
} catch {
|
|
62
|
+
// ignore malformed injected URL
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
return new URL(RequestContext.request.url).href;
|
|
67
|
+
} catch {
|
|
68
|
+
return props?.pageHref;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** Bridge `__pagePath` → `slug` when the CMS doesn't set slug explicitly. */
|
|
73
|
+
function pdpWithSlugFallback(props: any): Promise<any> {
|
|
74
|
+
if ((!props.slug || props.slug.length === 0) && props.__pagePath) {
|
|
75
|
+
props = { ...props, slug: props.__pagePath };
|
|
76
|
+
}
|
|
77
|
+
return productDetailsPage(props);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function createWakeCommerceLoaders(
|
|
81
|
+
options?: WakeCommerceLoadersOptions,
|
|
82
|
+
): Record<string, CommerceLoaderFn> {
|
|
83
|
+
const profiles = {
|
|
84
|
+
listing: options?.cacheProfiles?.listing ?? "listing",
|
|
85
|
+
product: options?.cacheProfiles?.product ?? "product",
|
|
86
|
+
search: options?.cacheProfiles?.search ?? "search",
|
|
87
|
+
static: options?.cacheProfiles?.static ?? "static",
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const cachedPDP = createCachedLoader(
|
|
91
|
+
"wake/productDetailsPage",
|
|
92
|
+
pdpWithSlugFallback,
|
|
93
|
+
profiles.product,
|
|
94
|
+
);
|
|
95
|
+
const _cachedPLP = createCachedLoader(
|
|
96
|
+
"wake/productListingPage",
|
|
97
|
+
productListingPage,
|
|
98
|
+
profiles.listing,
|
|
99
|
+
);
|
|
100
|
+
const cachedProductList = createCachedLoader("wake/productList", productList, profiles.listing);
|
|
101
|
+
const cachedSuggestion = createCachedLoader("wake/suggestion", suggestion, profiles.search);
|
|
102
|
+
const cachedRecommendations = createCachedLoader(
|
|
103
|
+
"wake/recommendations",
|
|
104
|
+
recommendations,
|
|
105
|
+
profiles.product,
|
|
106
|
+
);
|
|
107
|
+
const cachedShop = createCachedLoader("wake/shop", shopLoader, profiles.static);
|
|
108
|
+
const cachedPartners = createCachedLoader("wake/partners", partnersLoader, profiles.static);
|
|
109
|
+
|
|
110
|
+
// PLP wrapper: forward the resolved page URL as `pageHref` before caching.
|
|
111
|
+
const cachedPLP: CommerceLoaderFn = (props) =>
|
|
112
|
+
_cachedPLP({ ...props, pageHref: resolvePageHref(props) ?? props?.pageHref });
|
|
113
|
+
|
|
114
|
+
const loaders: Record<string, CommerceLoaderFn> = {
|
|
115
|
+
"wake/loaders/productDetailsPage.ts": cachedPDP,
|
|
116
|
+
"wake/loaders/productListingPage.ts": cachedPLP,
|
|
117
|
+
"wake/loaders/productList.ts": cachedProductList,
|
|
118
|
+
"wake/loaders/suggestion.ts": cachedSuggestion,
|
|
119
|
+
"wake/loaders/recommendations.ts": cachedRecommendations,
|
|
120
|
+
"wake/loaders/shop.ts": cachedShop,
|
|
121
|
+
"wake/loaders/partners.ts": cachedPartners,
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
// Register .ts-less aliases for invoke compatibility.
|
|
125
|
+
const withAliases: Record<string, CommerceLoaderFn> = { ...loaders };
|
|
126
|
+
for (const key of Object.keys(loaders)) {
|
|
127
|
+
if (key.endsWith(".ts")) {
|
|
128
|
+
withAliases[key.slice(0, -3)] = loaders[key];
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (options?.extra) {
|
|
133
|
+
Object.assign(withAliases, options.extra);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return withAliases;
|
|
137
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { withFetchTimeout } from "@decocms/blocks/sdk/fetchTimeout";
|
|
2
|
+
import { getWakeConfig } from "../client";
|
|
3
|
+
|
|
4
|
+
const fetchSafe = withFetchTimeout();
|
|
5
|
+
|
|
6
|
+
const BASE_SITEMAP_URL = "https://p-general-sitemap-public.s3.us-east-2.amazonaws.com/Sitemap";
|
|
7
|
+
|
|
8
|
+
const xmlHeader = '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">';
|
|
9
|
+
|
|
10
|
+
const includeSiteMaps = (currentXML: string, origin: string, includes?: string[]) => {
|
|
11
|
+
const siteMapIncludeTags: string[] = [];
|
|
12
|
+
|
|
13
|
+
for (const include of includes ?? []) {
|
|
14
|
+
siteMapIncludeTags.push(`
|
|
15
|
+
<sitemap>
|
|
16
|
+
<loc>${include.startsWith("/") ? `${origin}${include}` : include}</loc>
|
|
17
|
+
<lastmod>${new Date().toISOString().substring(0, 10)}</lastmod>
|
|
18
|
+
</sitemap>
|
|
19
|
+
`);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return siteMapIncludeTags.length > 0
|
|
23
|
+
? currentXML.replace(xmlHeader, `${xmlHeader}\n${siteMapIncludeTags.join("\n")}`)
|
|
24
|
+
: currentXML;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export interface Props {
|
|
28
|
+
include?: string[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @title Sitemap Proxy
|
|
33
|
+
*
|
|
34
|
+
* Returns a request handler that proxies the Wake public S3 sitemap for the
|
|
35
|
+
* configured account, rewriting the public URL to the current origin and
|
|
36
|
+
* optionally injecting extra `<sitemap>` entries.
|
|
37
|
+
*/
|
|
38
|
+
export default function Sitemap({ include }: Props = {}) {
|
|
39
|
+
return async (req: Request): Promise<Response> => {
|
|
40
|
+
const { account } = getWakeConfig();
|
|
41
|
+
|
|
42
|
+
if (!account) {
|
|
43
|
+
throw new Error("Missing account");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const publicUrl = `${BASE_SITEMAP_URL}/${encodeURIComponent(account)}/`;
|
|
47
|
+
|
|
48
|
+
const response = await fetchSafe(publicUrl);
|
|
49
|
+
|
|
50
|
+
const reqUrl = new URL(req.url);
|
|
51
|
+
const text = await response.text();
|
|
52
|
+
|
|
53
|
+
return new Response(
|
|
54
|
+
includeSiteMaps(text.replaceAll(publicUrl, `${reqUrl.origin}/`), reqUrl.origin, include),
|
|
55
|
+
{
|
|
56
|
+
headers: response.headers,
|
|
57
|
+
status: response.status,
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// App contract
|
|
2
|
+
|
|
3
|
+
// Cart actions
|
|
4
|
+
export { default as addItem } from "./actions/cart/addItem";
|
|
5
|
+
export { type CartItem, default as addItems } from "./actions/cart/addItems";
|
|
6
|
+
export { default as updateItemQuantity } from "./actions/cart/updateItemQuantity";
|
|
7
|
+
// Client & Config
|
|
8
|
+
export type { WakeConfig } from "./client";
|
|
9
|
+
export {
|
|
10
|
+
configureWake,
|
|
11
|
+
getBaseUrl,
|
|
12
|
+
getCheckoutUrl,
|
|
13
|
+
getWakeClient,
|
|
14
|
+
getWakeConfig,
|
|
15
|
+
initWakeFromBlocks,
|
|
16
|
+
setWakeFetch,
|
|
17
|
+
} from "./client";
|
|
18
|
+
// Commerce loaders factory (cached, CMS-registerable map)
|
|
19
|
+
export {
|
|
20
|
+
type CommerceLoaderFn,
|
|
21
|
+
createWakeCommerceLoaders,
|
|
22
|
+
type WakeCommerceLoadersOptions,
|
|
23
|
+
} from "./commerceLoaders";
|
|
24
|
+
// Cart / user / wishlist
|
|
25
|
+
export { default as cartLoader } from "./loaders/cart";
|
|
26
|
+
// Shop / partners
|
|
27
|
+
export { default as partnersLoader } from "./loaders/partners";
|
|
28
|
+
// Product loaders
|
|
29
|
+
export { default as productDetailsPageLoader } from "./loaders/productDetailsPage";
|
|
30
|
+
export { default as productListLoader } from "./loaders/productList";
|
|
31
|
+
export { default as productListingPageLoader } from "./loaders/productListingPage";
|
|
32
|
+
export type { WakeProxyRoute } from "./loaders/proxy";
|
|
33
|
+
export { default as proxyLoader } from "./loaders/proxy";
|
|
34
|
+
export { default as recommendationsLoader } from "./loaders/recommendations";
|
|
35
|
+
export { default as shopLoader } from "./loaders/shop";
|
|
36
|
+
export { default as suggestionLoader } from "./loaders/suggestion";
|
|
37
|
+
export { default as userLoader } from "./loaders/user";
|
|
38
|
+
export { default as wishlistLoader } from "./loaders/wishlist";
|
|
39
|
+
export { configure, type Props, type WakeState } from "./mod";
|
|
40
|
+
// Registry
|
|
41
|
+
export { WAKE_REGISTRY_ENTRY } from "./registry";
|
|
42
|
+
|
|
43
|
+
// Cookie utils
|
|
44
|
+
export { getCartCookie, setCartCookie, setClientCookie } from "./utils/cart";
|
|
45
|
+
export { getCookies, setCookie } from "./utils/cookies";
|
|
46
|
+
// GraphQL client + fetch instrumentation
|
|
47
|
+
export { extractGraphqlOperationName } from "./utils/graphqlOperationName";
|
|
48
|
+
export { type CreateWakeFetchOptions, createWakeFetch } from "./utils/instrumentedFetch";
|
|
49
|
+
export { wakeOperationRouter } from "./utils/operationRouter";
|
|
50
|
+
export { getPartnerCookie } from "./utils/partner";
|
|
51
|
+
// Transform helpers
|
|
52
|
+
export {
|
|
53
|
+
parseSlug,
|
|
54
|
+
toBreadcrumbList,
|
|
55
|
+
toFilters,
|
|
56
|
+
toProduct,
|
|
57
|
+
} from "./utils/transform";
|
|
58
|
+
export { getUserCookie } from "./utils/user";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { getWakeClient } from "../client";
|
|
2
|
+
import { handleAuthError } from "../utils/authError";
|
|
3
|
+
import { getCartCookie, setCartCookie } from "../utils/cart";
|
|
4
|
+
import { CreateCart, GetCart } from "../utils/graphql/queries";
|
|
5
|
+
import type {
|
|
6
|
+
CheckoutFragment,
|
|
7
|
+
CreateCartMutation,
|
|
8
|
+
GetCartQuery,
|
|
9
|
+
} from "../utils/graphql/storefront.graphql.gen";
|
|
10
|
+
import {
|
|
11
|
+
currentRequestHeaders,
|
|
12
|
+
currentResponseHeaders,
|
|
13
|
+
forwardedHeaders,
|
|
14
|
+
} from "../utils/requestCtx";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @title Wake Integration
|
|
18
|
+
* @description Cart loader
|
|
19
|
+
*/
|
|
20
|
+
const loader = async (): Promise<Partial<CheckoutFragment>> => {
|
|
21
|
+
const storefront = getWakeClient();
|
|
22
|
+
const cartId = getCartCookie(currentRequestHeaders());
|
|
23
|
+
const headers = forwardedHeaders();
|
|
24
|
+
|
|
25
|
+
let data: GetCartQuery | CreateCartMutation;
|
|
26
|
+
try {
|
|
27
|
+
data = cartId
|
|
28
|
+
? await storefront.query<GetCartQuery>(GetCart, { checkoutId: cartId }, headers)
|
|
29
|
+
: await storefront.query<CreateCartMutation>(CreateCart, {}, headers);
|
|
30
|
+
} catch (error: unknown) {
|
|
31
|
+
handleAuthError(error, "load cart");
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const checkoutId = data.checkout?.checkoutId;
|
|
35
|
+
|
|
36
|
+
if (checkoutId && cartId !== checkoutId) {
|
|
37
|
+
setCartCookie(currentResponseHeaders(), checkoutId);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return data.checkout ?? {};
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default loader;
|
|
44
|
+
|
|
45
|
+
// User-specific cart data; must not be cached/shared.
|
|
46
|
+
export const cache = "no-store";
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { getWakeClient } from "../client";
|
|
2
|
+
import { handleAuthError } from "../utils/authError";
|
|
3
|
+
import { GetPartners } from "../utils/graphql/queries";
|
|
4
|
+
import type { GetPartnersQuery } from "../utils/graphql/storefront.graphql.gen";
|
|
5
|
+
import { getPartnerCookie } from "../utils/partner";
|
|
6
|
+
import { currentRequestHeaders, forwardedHeaders } from "../utils/requestCtx";
|
|
7
|
+
import type { RequestURLParam } from "../utils/types";
|
|
8
|
+
|
|
9
|
+
export interface Props {
|
|
10
|
+
slug: RequestURLParam;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @title Wake Integration - Partners
|
|
15
|
+
* @description Partners loader
|
|
16
|
+
*/
|
|
17
|
+
const loader = async (props: Props): Promise<GetPartnersQuery["partners"]> => {
|
|
18
|
+
const storefront = getWakeClient();
|
|
19
|
+
const { slug } = props;
|
|
20
|
+
|
|
21
|
+
const headers = forwardedHeaders();
|
|
22
|
+
|
|
23
|
+
// Guard against missing slug to prevent querying with [undefined]
|
|
24
|
+
if (!slug) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let data: GetPartnersQuery | undefined;
|
|
29
|
+
try {
|
|
30
|
+
data = await storefront.query<GetPartnersQuery>(
|
|
31
|
+
GetPartners,
|
|
32
|
+
{ first: 1, alias: [slug] },
|
|
33
|
+
headers,
|
|
34
|
+
);
|
|
35
|
+
} catch (error: unknown) {
|
|
36
|
+
handleAuthError(error, "load partner information");
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return data?.partners ?? undefined;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const cache = "stale-while-revalidate";
|
|
43
|
+
|
|
44
|
+
export const cacheKey = (props: Props): string | null => {
|
|
45
|
+
// Don't cache if no slug is provided
|
|
46
|
+
if (!props.slug) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Don't cache if partner cookie is present - partner-specific content should not be cached
|
|
51
|
+
if (getPartnerCookie(currentRequestHeaders())) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return `wake:partners:${props.slug}:1`;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default loader;
|