@shopify/hydrogen 1.6.1 → 1.6.2
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/dist/esnext/entry-server.js +6 -7
- package/dist/esnext/foundation/HydrogenRequest/HydrogenRequest.server.js +4 -2
- package/dist/esnext/foundation/fetchSync/server/fetchSync.d.ts +1 -1
- package/dist/esnext/foundation/fetchSync/server/fetchSync.js +18 -5
- package/dist/esnext/foundation/useQuery/hooks.d.ts +2 -1
- package/dist/esnext/foundation/useQuery/hooks.js +2 -2
- package/dist/esnext/foundation/useUrl/useUrl.js +3 -3
- package/dist/esnext/framework/plugins/vite-plugin-hydrogen-config.js +1 -5
- package/dist/esnext/hooks/useShopQuery/hooks.js +46 -57
- package/dist/esnext/index.d.ts +1 -0
- package/dist/esnext/index.js +1 -0
- package/dist/esnext/utilities/bot-ua.js +4 -0
- package/dist/esnext/utilities/hash.js +0 -4
- package/dist/esnext/utilities/parse.d.ts +3 -0
- package/dist/esnext/utilities/parse.js +16 -0
- package/dist/esnext/utilities/storefrontApi.d.ts +1 -0
- package/dist/esnext/utilities/storefrontApi.js +15 -0
- package/dist/esnext/version.d.ts +1 -1
- package/dist/esnext/version.js +1 -1
- package/dist/node/framework/plugins/vite-plugin-hydrogen-config.js +1 -5
- package/package.json +2 -1
|
@@ -14,7 +14,7 @@ import { getTemplate } from './utilities/template.js';
|
|
|
14
14
|
import { Analytics } from './foundation/Analytics/Analytics.server.js';
|
|
15
15
|
import { DevTools } from './foundation/DevTools/DevTools.server.js';
|
|
16
16
|
import { getSyncSessionApi } from './foundation/session/session.js';
|
|
17
|
-
import {
|
|
17
|
+
import { parseState } from './utilities/parse.js';
|
|
18
18
|
import { htmlEncode } from './utilities/index.js';
|
|
19
19
|
import { generateUUID } from './utilities/random.js';
|
|
20
20
|
import { splitCookiesString } from 'set-cookie-parser';
|
|
@@ -153,12 +153,11 @@ async function processRequest(handleRequest, App, url, request, sessionApi, opti
|
|
|
153
153
|
})
|
|
154
154
|
: apiResponse;
|
|
155
155
|
}
|
|
156
|
-
const state =
|
|
157
|
-
|
|
158
|
-
:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
};
|
|
156
|
+
const state = parseState(url);
|
|
157
|
+
if (!state) {
|
|
158
|
+
postRequestTasks(isRSCRequest ? 'rsc' : 'ssr', 400, request, response, true);
|
|
159
|
+
return new Response(`Invalid RSC state`, { status: 400 });
|
|
160
|
+
}
|
|
162
161
|
const rsc = runRSC({ App, state, log, request, response });
|
|
163
162
|
if (isRSCRequest) {
|
|
164
163
|
const buffered = await bufferReadableStream(rsc.readable.getReader());
|
|
@@ -2,7 +2,7 @@ import { getTime } from '../../utilities/timing.js';
|
|
|
2
2
|
import { hashKey } from '../../utilities/hash.js';
|
|
3
3
|
import { HelmetData as HeadData } from 'react-helmet-async';
|
|
4
4
|
import { RSC_PATHNAME } from '../../constants.js';
|
|
5
|
-
import {
|
|
5
|
+
import { parseState } from '../../utilities/parse.js';
|
|
6
6
|
import { generateUUID } from '../../utilities/random.js';
|
|
7
7
|
// Stores queries by url or '*'
|
|
8
8
|
const preloadCache = new Map();
|
|
@@ -197,7 +197,9 @@ function getInitFromNodeRequest(request) {
|
|
|
197
197
|
}
|
|
198
198
|
function normalizeUrl(rawUrl) {
|
|
199
199
|
const url = new URL(rawUrl);
|
|
200
|
-
const state =
|
|
200
|
+
const state = parseState(url);
|
|
201
|
+
if (!state)
|
|
202
|
+
return '';
|
|
201
203
|
const normalizedUrl = new URL(state?.pathname ?? '', url.origin);
|
|
202
204
|
normalizedUrl.search = state?.search;
|
|
203
205
|
return normalizedUrl.toString();
|
|
@@ -5,4 +5,4 @@ import { ResponseSync } from '../ResponseSync.js';
|
|
|
5
5
|
* It's designed similar to the [Web API's `fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch), only in a way
|
|
6
6
|
* that supports [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html).
|
|
7
7
|
*/
|
|
8
|
-
export declare function fetchSync(
|
|
8
|
+
export declare function fetchSync(originalUrl: string, options?: Omit<RequestInit, 'cache'> & HydrogenUseQueryOptions): ResponseSync;
|
|
@@ -1,18 +1,31 @@
|
|
|
1
1
|
import { useQuery } from '../../useQuery/index.js';
|
|
2
|
-
import { useUrl } from '../../useUrl/index.js';
|
|
3
2
|
import { ResponseSync } from '../ResponseSync.js';
|
|
4
3
|
/**
|
|
5
4
|
* The `fetchSync` hook makes API requests and is the recommended way to make simple fetch calls on the server and the client.
|
|
6
5
|
* It's designed similar to the [Web API's `fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch), only in a way
|
|
7
6
|
* that supports [Suspense](https://reactjs.org/docs/concurrent-mode-suspense.html).
|
|
8
7
|
*/
|
|
9
|
-
export function fetchSync(
|
|
8
|
+
export function fetchSync(originalUrl, options) {
|
|
9
|
+
let requestUrl;
|
|
10
|
+
try {
|
|
11
|
+
requestUrl = new URL(originalUrl);
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
// The URL is not valid, and the user likely tried to call a relative path in the same app.
|
|
15
|
+
if (error instanceof TypeError && originalUrl.startsWith('/')) {
|
|
16
|
+
throw new TypeError(`fetchSync() was called with a relative URL (${originalUrl}). Please use an absolute URL instead.\n` +
|
|
17
|
+
`- If you're trying to fetch a relative path in the same app, query the data source directly from the server instead.\n` +
|
|
18
|
+
`- If you're calling fetchSync() within a client component, use a conditional state like useEffect() to ` +
|
|
19
|
+
`prevent it from being called on the server during pre-render.`);
|
|
20
|
+
}
|
|
21
|
+
// The URL is not valid for some other reason.
|
|
22
|
+
throw error;
|
|
23
|
+
}
|
|
24
|
+
const url = requestUrl.toString();
|
|
10
25
|
const { cache, preload, shouldCacheResponse, ...requestInit } = options ?? {};
|
|
11
26
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
12
|
-
const { origin } = useUrl();
|
|
13
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
14
27
|
const { data, error } = useQuery([url, requestInit], async () => {
|
|
15
|
-
const response = await globalThis.fetch(
|
|
28
|
+
const response = await globalThis.fetch(url, requestInit);
|
|
16
29
|
return ResponseSync.toSerializable(response);
|
|
17
30
|
}, {
|
|
18
31
|
cache,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CachingStrategy, PreloadOptions, QueryKey } from '../../types.js';
|
|
2
|
+
import type { HydrogenRequest } from '../HydrogenRequest/HydrogenRequest.server.js';
|
|
2
3
|
export interface HydrogenUseQueryOptions {
|
|
3
4
|
/** The [caching strategy](https://shopify.dev/custom-storefronts/hydrogen/framework/cache#caching-strategies) to help you
|
|
4
5
|
* determine which cache control header to set.
|
|
@@ -28,7 +29,7 @@ export declare function useQuery<T>(
|
|
|
28
29
|
/** A string or array to uniquely identify the current query. */
|
|
29
30
|
key: QueryKey,
|
|
30
31
|
/** An asynchronous query function like `fetch` which returns data. */
|
|
31
|
-
queryFn: () => Promise<T>,
|
|
32
|
+
queryFn: (request: HydrogenRequest) => Promise<T>,
|
|
32
33
|
/** The options to manage the cache behavior of the sub-request. */
|
|
33
34
|
queryOptions?: HydrogenUseQueryOptions): {
|
|
34
35
|
data?: undefined;
|
|
@@ -73,7 +73,7 @@ function cachedQueryFnBuilder(key, generateNewOutput, queryOptions) {
|
|
|
73
73
|
maxAge: 10,
|
|
74
74
|
}));
|
|
75
75
|
try {
|
|
76
|
-
const output = await generateNewOutput();
|
|
76
|
+
const output = await generateNewOutput(request);
|
|
77
77
|
if (shouldCacheResponse(output)) {
|
|
78
78
|
await setItemInCache(key, output, resolvedQueryOptions?.cache);
|
|
79
79
|
}
|
|
@@ -90,7 +90,7 @@ function cachedQueryFnBuilder(key, generateNewOutput, queryOptions) {
|
|
|
90
90
|
}
|
|
91
91
|
return output;
|
|
92
92
|
}
|
|
93
|
-
const newOutput = await generateNewOutput();
|
|
93
|
+
const newOutput = await generateNewOutput(request);
|
|
94
94
|
/**
|
|
95
95
|
* Important: Do this async
|
|
96
96
|
*/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useContext, useMemo } from 'react';
|
|
2
2
|
import { RSC_PATHNAME } from '../../constants.js';
|
|
3
|
-
import {
|
|
3
|
+
import { parseState } from '../../utilities/parse.js';
|
|
4
4
|
import { RouterContext } from '../Router/BrowserRouter.client.js';
|
|
5
5
|
import { useEnvContext, META_ENV_SSR } from '../ssr-interop.js';
|
|
6
6
|
/**
|
|
@@ -11,8 +11,8 @@ export function useUrl() {
|
|
|
11
11
|
const serverUrl = new URL(useEnvContext((req) => req.url) // eslint-disable-line react-hooks/rules-of-hooks
|
|
12
12
|
);
|
|
13
13
|
if (serverUrl.pathname === RSC_PATHNAME) {
|
|
14
|
-
const state =
|
|
15
|
-
const parsedUrl = `${serverUrl.origin}${state
|
|
14
|
+
const state = parseState(serverUrl);
|
|
15
|
+
const parsedUrl = `${serverUrl.origin}${state?.pathname ?? ''}${state?.search ?? ''}`;
|
|
16
16
|
return new URL(parsedUrl);
|
|
17
17
|
}
|
|
18
18
|
return new URL(serverUrl);
|
|
@@ -64,10 +64,7 @@ export default (pluginOptions) => {
|
|
|
64
64
|
// Reload when updating local Hydrogen lib
|
|
65
65
|
server: process.env.LOCAL_DEV && {
|
|
66
66
|
watch: {
|
|
67
|
-
ignored: [
|
|
68
|
-
'!**/node_modules/@shopify/hydrogen/**',
|
|
69
|
-
'!**/node_modules/@shopify/hydrogen-ui/**',
|
|
70
|
-
],
|
|
67
|
+
ignored: ['!**/node_modules/@shopify/hydrogen/**'],
|
|
71
68
|
},
|
|
72
69
|
},
|
|
73
70
|
optimizeDeps: {
|
|
@@ -75,7 +72,6 @@ export default (pluginOptions) => {
|
|
|
75
72
|
'@shopify/hydrogen',
|
|
76
73
|
'@shopify/hydrogen/client',
|
|
77
74
|
'@shopify/hydrogen/entry-client',
|
|
78
|
-
'@shopify/hydrogen-ui',
|
|
79
75
|
],
|
|
80
76
|
include: [
|
|
81
77
|
/**
|
|
@@ -1,24 +1,18 @@
|
|
|
1
|
+
/* eslint-disable react-hooks/rules-of-hooks */
|
|
1
2
|
import { useShop } from '../../foundation/useShop/index.js';
|
|
2
3
|
import { getLoggerWithContext } from '../../utilities/log/index.js';
|
|
3
4
|
import { graphqlRequestBody } from '../../utilities/index.js';
|
|
4
5
|
import { useServerRequest } from '../../foundation/ServerRequestProvider/index.js';
|
|
5
6
|
import { injectGraphQLTracker } from '../../utilities/graphql-tracker.js';
|
|
6
7
|
import { sendMessageToClient } from '../../utilities/devtools.js';
|
|
7
|
-
import { fetchSync } from '../../foundation/fetchSync/server/fetchSync.js';
|
|
8
8
|
import { META_ENV_SSR } from '../../foundation/ssr-interop.js';
|
|
9
9
|
import { getStorefrontApiRequestHeaders } from '../../utilities/storefrontApi.js';
|
|
10
10
|
import { parseJSON } from '../../utilities/parse.js';
|
|
11
|
+
import { useQuery } from '../../foundation/useQuery/hooks.js';
|
|
11
12
|
// Check if the response body has GraphQL errors
|
|
12
13
|
// https://spec.graphql.org/June2018/#sec-Response-Format
|
|
13
|
-
const shouldCacheResponse = (
|
|
14
|
-
|
|
15
|
-
return !parseJSON(body)?.errors;
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
// If we can't parse the response, then assume
|
|
19
|
-
// an error and don't cache the response
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
14
|
+
const shouldCacheResponse = (body) => {
|
|
15
|
+
return !body?.errors;
|
|
22
16
|
};
|
|
23
17
|
/**
|
|
24
18
|
* The `useShopQuery` hook allows you to make server-only GraphQL queries to the Storefront API. It must be a descendent of a `ShopifyProvider` component.
|
|
@@ -33,48 +27,61 @@ export function useShopQuery({ query, variables = {}, cache, preload = false, })
|
|
|
33
27
|
if (!META_ENV_SSR) {
|
|
34
28
|
throw new Error('Shopify Storefront API requests should only be made from the server.');
|
|
35
29
|
}
|
|
36
|
-
const serverRequest = useServerRequest();
|
|
30
|
+
const serverRequest = useServerRequest();
|
|
37
31
|
const log = getLoggerWithContext(serverRequest);
|
|
32
|
+
const { storeDomain, storefrontApiVersion, storefrontToken, storefrontId, privateStorefrontToken, } = useShop();
|
|
38
33
|
const body = query ? graphqlRequestBody(query, variables) : '';
|
|
39
|
-
const {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
preload,
|
|
49
|
-
shouldCacheResponse,
|
|
34
|
+
const { data, error } = useQuery([storeDomain, storefrontApiVersion, body], async (request) => {
|
|
35
|
+
const { url, requestInit } = useCreateShopRequest({
|
|
36
|
+
body,
|
|
37
|
+
request,
|
|
38
|
+
storeDomain,
|
|
39
|
+
storefrontToken,
|
|
40
|
+
storefrontApiVersion,
|
|
41
|
+
storefrontId,
|
|
42
|
+
privateStorefrontToken,
|
|
50
43
|
});
|
|
51
|
-
|
|
44
|
+
const response = await fetch(url, requestInit);
|
|
45
|
+
const text = await response.text();
|
|
52
46
|
try {
|
|
53
|
-
data =
|
|
47
|
+
const data = parseJSON(text);
|
|
48
|
+
/**
|
|
49
|
+
* GraphQL errors get printed to the console but ultimately
|
|
50
|
+
* get returned to the consumer.
|
|
51
|
+
*/
|
|
52
|
+
if (data?.errors) {
|
|
53
|
+
const errors = Array.isArray(data.errors)
|
|
54
|
+
? data.errors
|
|
55
|
+
: [data.errors];
|
|
56
|
+
const requestId = response?.headers?.get('x-request-id') ?? '';
|
|
57
|
+
for (const error of errors) {
|
|
58
|
+
if (__HYDROGEN_DEV__ && !__HYDROGEN_TEST__) {
|
|
59
|
+
throw new Error(`Storefront API GraphQL Error: ${error.message}.\nRequest id: ${requestId}`);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
log.error('Storefront API GraphQL Error', error, 'Storefront API GraphQL request id', requestId);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
log.error(`Storefront API GraphQL error count: ${errors.length}`);
|
|
66
|
+
}
|
|
67
|
+
return data;
|
|
54
68
|
}
|
|
55
69
|
catch (error) {
|
|
56
70
|
if (response.headers.get('content-length')) {
|
|
57
|
-
|
|
71
|
+
throw new Error(`Unable to parse response (x-request-id: ${response.headers.get('x-request-id')}):\n${text}`);
|
|
58
72
|
}
|
|
59
73
|
else {
|
|
60
|
-
|
|
74
|
+
throw new Error(`${response.status} ${response.statusText} (x-request-id: ${response.headers.get('x-request-id')})`);
|
|
61
75
|
}
|
|
62
76
|
}
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
// Pass-through thrown promise for Suspense functionality
|
|
66
|
-
if (error?.then) {
|
|
67
|
-
throw error;
|
|
68
|
-
}
|
|
69
|
-
useQueryError = error;
|
|
70
|
-
}
|
|
77
|
+
}, { cache, preload, shouldCacheResponse });
|
|
71
78
|
/**
|
|
72
79
|
* The fetch request itself failed, so we handle that differently than a GraphQL error
|
|
73
80
|
*/
|
|
74
|
-
if (
|
|
75
|
-
const errorMessage = createErrorMessage(
|
|
81
|
+
if (error) {
|
|
82
|
+
const errorMessage = createErrorMessage(error);
|
|
76
83
|
log.error(errorMessage);
|
|
77
|
-
log.error(
|
|
84
|
+
log.error(error);
|
|
78
85
|
if (__HYDROGEN_DEV__ && !__HYDROGEN_TEST__) {
|
|
79
86
|
throw new Error(errorMessage);
|
|
80
87
|
}
|
|
@@ -83,23 +90,6 @@ export function useShopQuery({ query, variables = {}, cache, preload = false, })
|
|
|
83
90
|
throw new Error(`The fetch attempt failed; there was an issue connecting to the data source.`);
|
|
84
91
|
}
|
|
85
92
|
}
|
|
86
|
-
/**
|
|
87
|
-
* GraphQL errors get printed to the console but ultimately
|
|
88
|
-
* get returned to the consumer.
|
|
89
|
-
*/
|
|
90
|
-
if (data?.errors) {
|
|
91
|
-
const errors = Array.isArray(data.errors) ? data.errors : [data.errors];
|
|
92
|
-
const requestId = response?.headers?.get('x-request-id') ?? '';
|
|
93
|
-
for (const error of errors) {
|
|
94
|
-
if (__HYDROGEN_DEV__ && !__HYDROGEN_TEST__) {
|
|
95
|
-
throw new Error(`Storefront API GraphQL Error: ${error.message}.\nRequest id: ${requestId}`);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
log.error('Storefront API GraphQL Error', error, 'Storefront API GraphQL request id', requestId);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
log.error(`Storefront API GraphQL error count: ${errors.length}`);
|
|
102
|
-
}
|
|
103
93
|
if (__HYDROGEN_DEV__ &&
|
|
104
94
|
(log.options().showUnusedQueryProperties ||
|
|
105
95
|
serverRequest.ctx.hydrogenConfig?.__EXPERIMENTAL__devTools) &&
|
|
@@ -144,9 +134,7 @@ export function useShopQuery({ query, variables = {}, cache, preload = false, })
|
|
|
144
134
|
}
|
|
145
135
|
return data;
|
|
146
136
|
}
|
|
147
|
-
function useCreateShopRequest(body) {
|
|
148
|
-
const { storeDomain, storefrontToken, storefrontApiVersion, storefrontId, privateStorefrontToken, } = useShop();
|
|
149
|
-
const request = useServerRequest();
|
|
137
|
+
function useCreateShopRequest({ body, request, storeDomain, storefrontToken, storefrontApiVersion, storefrontId, privateStorefrontToken, }) {
|
|
150
138
|
const buyerIp = request.getBuyerIp();
|
|
151
139
|
let headers = {
|
|
152
140
|
'X-SDK-Variant': 'hydrogen',
|
|
@@ -185,3 +173,4 @@ function createErrorMessage(fetchError) {
|
|
|
185
173
|
return `Failed to connect to the Storefront API: ${fetchError.message}`;
|
|
186
174
|
}
|
|
187
175
|
}
|
|
176
|
+
/* eslint-enable react-hooks/rules-of-hooks */
|
package/dist/esnext/index.d.ts
CHANGED
|
@@ -42,3 +42,4 @@ export { type HydrogenRequest } from './foundation/HydrogenRequest/HydrogenReque
|
|
|
42
42
|
export { type HydrogenResponse } from './foundation/HydrogenResponse/HydrogenResponse.server.js';
|
|
43
43
|
export { type HydrogenRouteProps, type CachingStrategy } from './types.js';
|
|
44
44
|
export { type ResourceGetter as HydrogenApiRoute, RequestOptions as HydrogenApiRouteOptions, } from './utilities/apiRoutes.js';
|
|
45
|
+
export { getOnlineStorefrontHeaders } from './utilities/storefrontApi.js';
|
package/dist/esnext/index.js
CHANGED
|
@@ -38,3 +38,4 @@ export { CartQuery } from './components/CartProvider/cart-queries.js';
|
|
|
38
38
|
* Override the client version of `fetchSync` with the server version.
|
|
39
39
|
*/
|
|
40
40
|
export { fetchSync } from './foundation/fetchSync/server/fetchSync.js';
|
|
41
|
+
export { getOnlineStorefrontHeaders } from './utilities/storefrontApi.js';
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
const botUserAgents = [
|
|
7
7
|
'AdsBot-Google',
|
|
8
|
+
'AdsBot-Google-Mobile',
|
|
8
9
|
'applebot',
|
|
9
10
|
'Baiduspider',
|
|
10
11
|
'baiduspider',
|
|
@@ -23,6 +24,9 @@ const botUserAgents = [
|
|
|
23
24
|
'facebookexternalhit',
|
|
24
25
|
'Google-PageRenderer',
|
|
25
26
|
'Googlebot',
|
|
27
|
+
'Googlebot-Image',
|
|
28
|
+
'Googlebot-News',
|
|
29
|
+
'Googlebot-Video',
|
|
26
30
|
'googleweblight',
|
|
27
31
|
'ia_archive',
|
|
28
32
|
'LinkedInBot',
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { STOREFRONT_API_BUYER_IP_HEADER } from '../constants.js';
|
|
2
1
|
export function hashKey(queryKey) {
|
|
3
2
|
const rawKeys = Array.isArray(queryKey) ? queryKey : [queryKey];
|
|
4
3
|
let hash = '';
|
|
@@ -13,9 +12,6 @@ export function hashKey(queryKey) {
|
|
|
13
12
|
// fallback to a safer (but slower) stringify.
|
|
14
13
|
if (!!key.body && typeof key.body === 'string') {
|
|
15
14
|
hash += key.body;
|
|
16
|
-
if (!!key.headers && key.headers[STOREFRONT_API_BUYER_IP_HEADER]) {
|
|
17
|
-
hash += key.headers[STOREFRONT_API_BUYER_IP_HEADER];
|
|
18
|
-
}
|
|
19
15
|
}
|
|
20
16
|
else {
|
|
21
17
|
hash += JSON.stringify(key);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RSC_PATHNAME } from '../constants.js';
|
|
1
2
|
export function parseJSON(json) {
|
|
2
3
|
if (String(json).includes('__proto__'))
|
|
3
4
|
return JSON.parse(json, noproto);
|
|
@@ -7,3 +8,18 @@ function noproto(k, v) {
|
|
|
7
8
|
if (k !== '__proto__')
|
|
8
9
|
return v;
|
|
9
10
|
}
|
|
11
|
+
export function parseState(url) {
|
|
12
|
+
try {
|
|
13
|
+
const { pathname, search } = url;
|
|
14
|
+
const state = pathname === RSC_PATHNAME
|
|
15
|
+
? parseJSON(url.searchParams.get('state') ?? '{}')
|
|
16
|
+
: { pathname, search };
|
|
17
|
+
return Object.fromEntries(Object.entries(state).map(([key, value]) => [
|
|
18
|
+
decodeURIComponent(key ?? ''),
|
|
19
|
+
decodeURIComponent(value ?? ''),
|
|
20
|
+
]));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Do not throw to prevent unhandled errors
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -5,3 +5,4 @@ export declare function getStorefrontApiRequestHeaders({ buyerIp, publicStorefro
|
|
|
5
5
|
storefrontId: string | undefined;
|
|
6
6
|
}): Record<string, any>;
|
|
7
7
|
export declare function getOxygenVariable(key: string): any;
|
|
8
|
+
export declare function getOnlineStorefrontHeaders(request: Request, origin: string): Headers;
|
|
@@ -46,3 +46,18 @@ export function getStorefrontApiRequestHeaders({ buyerIp, publicStorefrontToken,
|
|
|
46
46
|
export function getOxygenVariable(key) {
|
|
47
47
|
return typeof Oxygen !== 'undefined' ? Oxygen?.env?.[key] : null;
|
|
48
48
|
}
|
|
49
|
+
export function getOnlineStorefrontHeaders(request, origin) {
|
|
50
|
+
const clientIP = request.headers.get('X-Shopify-Client-IP');
|
|
51
|
+
const clientIPSig = request.headers.get('X-Shopify-Client-IP-Sig');
|
|
52
|
+
const headers = new Headers();
|
|
53
|
+
for (const [key, value] of request.headers.entries()) {
|
|
54
|
+
headers.append(key, swapHostname(value, { hostname: new URL(request.url).host, origin }));
|
|
55
|
+
}
|
|
56
|
+
if (!__HYDROGEN_DEV__ && (!clientIP || !clientIPSig)) {
|
|
57
|
+
log.warn('Proxying the online store is only available in Oxygen. This request is likely to be throttled.');
|
|
58
|
+
}
|
|
59
|
+
return headers;
|
|
60
|
+
}
|
|
61
|
+
function swapHostname(str, { hostname, origin }) {
|
|
62
|
+
return str.replaceAll(hostname, origin);
|
|
63
|
+
}
|
package/dist/esnext/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const LIB_VERSION = "1.6.
|
|
1
|
+
export declare const LIB_VERSION = "1.6.2";
|
package/dist/esnext/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const LIB_VERSION = '1.6.
|
|
1
|
+
export const LIB_VERSION = '1.6.2';
|
|
@@ -69,10 +69,7 @@ exports.default = (pluginOptions) => {
|
|
|
69
69
|
// Reload when updating local Hydrogen lib
|
|
70
70
|
server: process.env.LOCAL_DEV && {
|
|
71
71
|
watch: {
|
|
72
|
-
ignored: [
|
|
73
|
-
'!**/node_modules/@shopify/hydrogen/**',
|
|
74
|
-
'!**/node_modules/@shopify/hydrogen-ui/**',
|
|
75
|
-
],
|
|
72
|
+
ignored: ['!**/node_modules/@shopify/hydrogen/**'],
|
|
76
73
|
},
|
|
77
74
|
},
|
|
78
75
|
optimizeDeps: {
|
|
@@ -80,7 +77,6 @@ exports.default = (pluginOptions) => {
|
|
|
80
77
|
'@shopify/hydrogen',
|
|
81
78
|
'@shopify/hydrogen/client',
|
|
82
79
|
'@shopify/hydrogen/entry-client',
|
|
83
|
-
'@shopify/hydrogen-ui',
|
|
84
80
|
],
|
|
85
81
|
include: [
|
|
86
82
|
/**
|
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=14"
|
|
9
9
|
},
|
|
10
|
-
"version": "1.6.
|
|
10
|
+
"version": "1.6.2",
|
|
11
11
|
"description": "Modern custom Shopify storefronts",
|
|
12
12
|
"license": "MIT",
|
|
13
13
|
"main": "dist/esnext/index.js",
|
|
@@ -100,6 +100,7 @@
|
|
|
100
100
|
"@types/set-cookie-parser": "^2.4.2",
|
|
101
101
|
"@types/uuid": "^8.3.4",
|
|
102
102
|
"@types/ws": "^8.2.0",
|
|
103
|
+
"@vitest/coverage-c8": "^0.24.3",
|
|
103
104
|
"babel-loader": "^8.2.2",
|
|
104
105
|
"cpy-cli": "^3.1.0",
|
|
105
106
|
"eslint-plugin-import": "^2.26.0",
|