hydrogen-sfdgspsdmq-test1 0.0.1-security → 2024.1.14

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.

Potentially problematic release.


This version of hydrogen-sfdgspsdmq-test1 might be problematic. Click here for more details.

@@ -0,0 +1,3491 @@
1
+ import { createStorefrontClient as createStorefrontClient$1, SHOPIFY_STOREFRONT_ID_HEADER, getShopifyCookies, SHOPIFY_Y, SHOPIFY_STOREFRONT_Y_HEADER, SHOPIFY_S, SHOPIFY_STOREFRONT_S_HEADER, flattenConnection, ShopPayButton as ShopPayButton$1 } from '@shopify/hydrogen-react';
2
+ export { AnalyticsEventName, AnalyticsPageType, ExternalVideo, IMAGE_FRAGMENT, Image, MediaFile, ModelViewer, Money, ShopifySalesChannel, Video, customerAccountApiCustomScalars, flattenConnection, getClientBrowserParameters, getShopifyCookies, parseGid, parseMetafield, sendShopifyAnalytics, storefrontApiCustomScalars, useLoadScript, useMoney, useShopifyCookies } from '@shopify/hydrogen-react';
3
+ import { lazy, createContext, forwardRef, useMemo, createElement, Suspense, Fragment, useRef, useEffect, useContext } from 'react';
4
+ import { useMatches, useLocation, useNavigation, Link, useNavigate, useFetcher, useFetchers } from '@remix-run/react';
5
+ import { jsx, jsxs, Fragment as Fragment$1 } from 'react/jsx-runtime';
6
+ import cspBuilder from 'content-security-policy-builder';
7
+
8
+ // src/storefront.ts
9
+
10
+ // src/utils/hash.ts
11
+ function hashKey(queryKey) {
12
+ const rawKeys = Array.isArray(queryKey) ? queryKey : [queryKey];
13
+ let hash = "";
14
+ for (const key of rawKeys) {
15
+ if (key != null) {
16
+ if (typeof key === "object") {
17
+ hash += JSON.stringify(key);
18
+ } else {
19
+ hash += key.toString();
20
+ }
21
+ }
22
+ }
23
+ return encodeURIComponent(hash);
24
+ }
25
+
26
+ // src/cache/strategies.ts
27
+ var PUBLIC = "public";
28
+ var PRIVATE = "private";
29
+ var NO_STORE = "no-store";
30
+ var optionMapping = {
31
+ maxAge: "max-age",
32
+ staleWhileRevalidate: "stale-while-revalidate",
33
+ sMaxAge: "s-maxage",
34
+ staleIfError: "stale-if-error"
35
+ };
36
+ function generateCacheControlHeader(cacheOptions) {
37
+ const cacheControl = [];
38
+ Object.keys(cacheOptions).forEach((key) => {
39
+ if (key === "mode") {
40
+ cacheControl.push(cacheOptions[key]);
41
+ } else if (optionMapping[key]) {
42
+ cacheControl.push(
43
+ `${optionMapping[key]}=${cacheOptions[key]}`
44
+ );
45
+ }
46
+ });
47
+ return cacheControl.join(", ");
48
+ }
49
+ function CacheNone() {
50
+ return {
51
+ mode: NO_STORE
52
+ };
53
+ }
54
+ function guardExpirableModeType(overrideOptions) {
55
+ if (overrideOptions?.mode && overrideOptions?.mode !== PUBLIC && overrideOptions?.mode !== PRIVATE) {
56
+ throw Error("'mode' must be either 'public' or 'private'");
57
+ }
58
+ }
59
+ function CacheShort(overrideOptions) {
60
+ guardExpirableModeType(overrideOptions);
61
+ return {
62
+ mode: PUBLIC,
63
+ maxAge: 1,
64
+ staleWhileRevalidate: 9,
65
+ ...overrideOptions
66
+ };
67
+ }
68
+ function CacheLong(overrideOptions) {
69
+ guardExpirableModeType(overrideOptions);
70
+ return {
71
+ mode: PUBLIC,
72
+ maxAge: 3600,
73
+ // 1 hour
74
+ staleWhileRevalidate: 82800,
75
+ // 23 Hours
76
+ ...overrideOptions
77
+ };
78
+ }
79
+ function CacheDefault(overrideOptions) {
80
+ guardExpirableModeType(overrideOptions);
81
+ return {
82
+ mode: PUBLIC,
83
+ maxAge: 1,
84
+ staleWhileRevalidate: 86399,
85
+ // 1 second less than 24 hours
86
+ ...overrideOptions
87
+ };
88
+ }
89
+ function CacheCustom(overrideOptions) {
90
+ return overrideOptions;
91
+ }
92
+
93
+ // src/utils/parse-json.ts
94
+ function parseJSON(json) {
95
+ if (String(json).includes("__proto__"))
96
+ return JSON.parse(json, noproto);
97
+ return JSON.parse(json);
98
+ }
99
+ function noproto(k, v) {
100
+ if (k !== "__proto__")
101
+ return v;
102
+ }
103
+ function getCacheControlSetting(userCacheOptions, options) {
104
+ if (userCacheOptions && options) {
105
+ return {
106
+ ...userCacheOptions,
107
+ ...options
108
+ };
109
+ } else {
110
+ return userCacheOptions || CacheDefault();
111
+ }
112
+ }
113
+ function generateDefaultCacheControlHeader(userCacheOptions) {
114
+ return generateCacheControlHeader(getCacheControlSetting(userCacheOptions));
115
+ }
116
+ async function getItem(cache, request) {
117
+ if (!cache)
118
+ return;
119
+ const response = await cache.match(request);
120
+ if (!response) {
121
+ return;
122
+ }
123
+ return response;
124
+ }
125
+ async function setItem(cache, request, response, userCacheOptions) {
126
+ if (!cache)
127
+ return;
128
+ const cacheControl = getCacheControlSetting(userCacheOptions);
129
+ const paddedCacheControlString = generateDefaultCacheControlHeader(
130
+ getCacheControlSetting(cacheControl, {
131
+ maxAge: (cacheControl.maxAge || 0) + (cacheControl.staleWhileRevalidate || 0)
132
+ })
133
+ );
134
+ const cacheControlString = generateDefaultCacheControlHeader(
135
+ getCacheControlSetting(cacheControl)
136
+ );
137
+ response.headers.set("cache-control", paddedCacheControlString);
138
+ response.headers.set("real-cache-control", cacheControlString);
139
+ response.headers.set("cache-put-date", (/* @__PURE__ */ new Date()).toUTCString());
140
+ await cache.put(request, response);
141
+ }
142
+ async function deleteItem(cache, request) {
143
+ if (!cache)
144
+ return;
145
+ await cache.delete(request);
146
+ }
147
+ function calculateAge(response, responseDate) {
148
+ const cacheControl = response.headers.get("real-cache-control");
149
+ let responseMaxAge = 0;
150
+ if (cacheControl) {
151
+ const maxAgeMatch = cacheControl.match(/max-age=(\d*)/);
152
+ if (maxAgeMatch && maxAgeMatch.length > 1) {
153
+ responseMaxAge = parseFloat(maxAgeMatch[1]);
154
+ }
155
+ }
156
+ const ageInMs = (/* @__PURE__ */ new Date()).valueOf() - new Date(responseDate).valueOf();
157
+ return [ageInMs / 1e3, responseMaxAge];
158
+ }
159
+ function isStale(request, response) {
160
+ const responseDate = response.headers.get("cache-put-date");
161
+ if (!responseDate) {
162
+ return false;
163
+ }
164
+ const [age, responseMaxAge] = calculateAge(response, responseDate);
165
+ const result = age > responseMaxAge;
166
+ return result;
167
+ }
168
+ var CacheAPI = {
169
+ get: getItem,
170
+ set: setItem,
171
+ delete: deleteItem,
172
+ generateDefaultCacheControlHeader,
173
+ isStale
174
+ };
175
+
176
+ // src/cache/sub-request.ts
177
+ function getKeyUrl(key) {
178
+ return `https://shopify.dev/?${key}`;
179
+ }
180
+ function getCacheOption(userCacheOptions) {
181
+ return userCacheOptions || CacheDefault();
182
+ }
183
+ async function getItemFromCache(cache, key) {
184
+ if (!cache)
185
+ return;
186
+ const url = getKeyUrl(key);
187
+ const request = new Request(url);
188
+ const response = await CacheAPI.get(cache, request);
189
+ if (!response) {
190
+ return;
191
+ }
192
+ const text = await response.text();
193
+ try {
194
+ return [parseJSON(text), response];
195
+ } catch {
196
+ return [text, response];
197
+ }
198
+ }
199
+ async function setItemInCache(cache, key, value, userCacheOptions) {
200
+ if (!cache)
201
+ return;
202
+ const url = getKeyUrl(key);
203
+ const request = new Request(url);
204
+ const response = new Response(JSON.stringify(value));
205
+ await CacheAPI.set(
206
+ cache,
207
+ request,
208
+ response,
209
+ getCacheOption(userCacheOptions)
210
+ );
211
+ }
212
+ function isStale2(key, response) {
213
+ return CacheAPI.isStale(new Request(getKeyUrl(key)), response);
214
+ }
215
+
216
+ // src/cache/fetch.ts
217
+ function toSerializableResponse(body, response) {
218
+ return [
219
+ body,
220
+ {
221
+ status: response.status,
222
+ statusText: response.statusText,
223
+ headers: Array.from(response.headers.entries())
224
+ }
225
+ ];
226
+ }
227
+ function fromSerializableResponse([body, init]) {
228
+ return [body, new Response(body, init)];
229
+ }
230
+ var checkGraphQLErrors = (body, response) => !body?.errors && response.status < 400;
231
+ var swrLock = /* @__PURE__ */ new Set();
232
+ async function runWithCache(cacheKey, actionFn, {
233
+ strategy = CacheShort(),
234
+ cacheInstance,
235
+ shouldCacheResult = () => true,
236
+ waitUntil,
237
+ debugInfo
238
+ }) {
239
+ const startTime = Date.now();
240
+ const key = hashKey([
241
+ // '__HYDROGEN_CACHE_ID__', // TODO purgeQueryCacheOnBuild
242
+ ...typeof cacheKey === "string" ? [cacheKey] : cacheKey
243
+ ]);
244
+ let debugData;
245
+ const addDebugData = (info) => {
246
+ debugData = {
247
+ displayName: info.displayName,
248
+ url: info.response?.url,
249
+ responseInit: {
250
+ status: info.response?.status || 0,
251
+ statusText: info.response?.statusText || "",
252
+ headers: Array.from(info.response?.headers.entries() || [])
253
+ }
254
+ };
255
+ };
256
+ const logSubRequestEvent2 = ({
257
+ result: result2,
258
+ cacheStatus,
259
+ overrideStartTime
260
+ }) => {
261
+ globalThis.__H2O_LOG_EVENT?.({
262
+ eventType: "subrequest",
263
+ startTime: overrideStartTime || startTime,
264
+ cacheStatus,
265
+ responsePayload: result2 && result2[0] || result2,
266
+ responseInit: result2 && result2[1] || debugData?.responseInit,
267
+ cache: {
268
+ status: cacheStatus,
269
+ strategy: generateCacheControlHeader(strategy || {}),
270
+ key
271
+ },
272
+ waitUntil,
273
+ ...debugInfo,
274
+ url: debugData?.url || debugInfo?.url || getKeyUrl(key),
275
+ displayName: debugInfo?.displayName || debugData?.displayName
276
+ });
277
+ } ;
278
+ if (!cacheInstance || !strategy || strategy.mode === NO_STORE) {
279
+ const result2 = await actionFn({ addDebugData });
280
+ logSubRequestEvent2?.({ result: result2 });
281
+ return result2;
282
+ }
283
+ const cachedItem = await getItemFromCache(cacheInstance, key);
284
+ if (cachedItem) {
285
+ const [cachedResult, cacheInfo] = cachedItem;
286
+ const cacheStatus = isStale2(key, cacheInfo) ? "STALE" : "HIT";
287
+ if (!swrLock.has(key) && cacheStatus === "STALE") {
288
+ swrLock.add(key);
289
+ const revalidatingPromise = Promise.resolve().then(async () => {
290
+ const revalidateStartTime = Date.now();
291
+ try {
292
+ const result2 = await actionFn({ addDebugData });
293
+ if (shouldCacheResult(result2)) {
294
+ await setItemInCache(cacheInstance, key, result2, strategy);
295
+ logSubRequestEvent2?.({
296
+ result: result2,
297
+ cacheStatus: "PUT",
298
+ overrideStartTime: revalidateStartTime
299
+ });
300
+ }
301
+ } catch (error) {
302
+ if (error.message) {
303
+ error.message = "SWR in sub-request failed: " + error.message;
304
+ }
305
+ console.error(error);
306
+ } finally {
307
+ swrLock.delete(key);
308
+ }
309
+ });
310
+ waitUntil?.(revalidatingPromise);
311
+ }
312
+ logSubRequestEvent2?.({
313
+ result: cachedResult,
314
+ cacheStatus
315
+ });
316
+ return cachedResult;
317
+ }
318
+ const result = await actionFn({ addDebugData });
319
+ logSubRequestEvent2?.({
320
+ result,
321
+ cacheStatus: "MISS"
322
+ });
323
+ if (shouldCacheResult(result)) {
324
+ const setItemInCachePromise = Promise.resolve().then(async () => {
325
+ const putStartTime = Date.now();
326
+ await setItemInCache(cacheInstance, key, result, strategy);
327
+ logSubRequestEvent2?.({
328
+ result,
329
+ cacheStatus: "PUT",
330
+ overrideStartTime: putStartTime
331
+ });
332
+ });
333
+ waitUntil?.(setItemInCachePromise);
334
+ }
335
+ return result;
336
+ }
337
+ async function fetchWithServerCache(url, requestInit, {
338
+ cacheInstance,
339
+ cache: cacheOptions,
340
+ cacheKey = [url, requestInit],
341
+ shouldCacheResponse = () => true,
342
+ waitUntil,
343
+ returnType = "json",
344
+ debugInfo
345
+ } = {}) {
346
+ if (!cacheOptions && (!requestInit.method || requestInit.method === "GET")) {
347
+ cacheOptions = CacheShort();
348
+ }
349
+ return runWithCache(
350
+ cacheKey,
351
+ async () => {
352
+ const response = await fetch(url, requestInit);
353
+ let data;
354
+ try {
355
+ data = await response[returnType]();
356
+ } catch {
357
+ try {
358
+ data = await response.text();
359
+ } catch {
360
+ return toSerializableResponse("", response);
361
+ }
362
+ }
363
+ return toSerializableResponse(data, response);
364
+ },
365
+ {
366
+ cacheInstance,
367
+ waitUntil,
368
+ strategy: cacheOptions ?? null,
369
+ debugInfo,
370
+ shouldCacheResult: (result) => shouldCacheResponse(...fromSerializableResponse(result))
371
+ }
372
+ ).then(fromSerializableResponse);
373
+ }
374
+
375
+ // src/constants.ts
376
+ var STOREFRONT_REQUEST_GROUP_ID_HEADER = "Custom-Storefront-Request-Group-ID";
377
+ var STOREFRONT_ACCESS_TOKEN_HEADER = "X-Shopify-Storefront-Access-Token";
378
+ var SDK_VARIANT_HEADER = "X-SDK-Variant";
379
+ var SDK_VARIANT_SOURCE_HEADER = "X-SDK-Variant-Source";
380
+ var SDK_VERSION_HEADER = "X-SDK-Version";
381
+
382
+ // src/utils/uuid.ts
383
+ function generateUUID() {
384
+ if (typeof crypto !== "undefined" && !!crypto.randomUUID) {
385
+ return crypto.randomUUID();
386
+ } else {
387
+ return `weak-${Math.random().toString(16).substring(2)}`;
388
+ }
389
+ }
390
+
391
+ // src/utils/warning.ts
392
+ var warnings = /* @__PURE__ */ new Set();
393
+ var warnOnce = (string) => {
394
+ if (!warnings.has(string)) {
395
+ console.warn(string);
396
+ warnings.add(string);
397
+ }
398
+ };
399
+
400
+ // src/version.ts
401
+ var LIB_VERSION = "2024.1.6";
402
+
403
+ // src/utils/graphql.ts
404
+ function minifyQuery(string) {
405
+ return string.replace(/\s*#.*$/gm, "").replace(/\s+/gm, " ").trim();
406
+ }
407
+ var IS_QUERY_RE = /(^|}\s)query[\s({]/im;
408
+ var IS_MUTATION_RE = /(^|}\s)mutation[\s({]/im;
409
+ function assertQuery(query, callerName) {
410
+ if (!IS_QUERY_RE.test(query)) {
411
+ throw new Error(`[h2:error:${callerName}] Can only execute queries`);
412
+ }
413
+ }
414
+ function assertMutation(query, callerName) {
415
+ if (!IS_MUTATION_RE.test(query)) {
416
+ throw new Error(`[h2:error:${callerName}] Can only execute mutations`);
417
+ }
418
+ }
419
+ var GraphQLError = class extends Error {
420
+ /**
421
+ * If an error can be associated to a particular point in the requested
422
+ * GraphQL document, it should contain a list of locations.
423
+ */
424
+ locations;
425
+ /**
426
+ * If an error can be associated to a particular field in the GraphQL result,
427
+ * it _must_ contain an entry with the key `path` that details the path of
428
+ * the response field which experienced the error. This allows clients to
429
+ * identify whether a null result is intentional or caused by a runtime error.
430
+ */
431
+ path;
432
+ /**
433
+ * Reserved for implementors to extend the protocol however they see fit,
434
+ * and hence there are no additional restrictions on its contents.
435
+ */
436
+ extensions;
437
+ constructor(message, options = {}) {
438
+ const h2Prefix = options.clientOperation ? `[h2:error:${options.clientOperation}] ` : "";
439
+ const enhancedMessage = h2Prefix + message + (options.requestId ? ` - Request ID: ${options.requestId}` : "");
440
+ super(enhancedMessage);
441
+ this.name = "GraphQLError";
442
+ this.extensions = options.extensions;
443
+ this.locations = options.locations;
444
+ this.path = options.path;
445
+ this.stack = options.stack || void 0;
446
+ try {
447
+ this.cause = JSON.stringify({
448
+ ...typeof options.cause === "object" ? options.cause : {},
449
+ requestId: options.requestId,
450
+ ...{
451
+ path: options.path,
452
+ extensions: options.extensions,
453
+ graphql: h2Prefix && options.query && {
454
+ query: options.query,
455
+ variables: JSON.stringify(options.queryVariables)
456
+ }
457
+ }
458
+ });
459
+ } catch {
460
+ if (options.cause)
461
+ this.cause = options.cause;
462
+ }
463
+ }
464
+ get [Symbol.toStringTag]() {
465
+ return this.name;
466
+ }
467
+ /**
468
+ * Note: `toString()` is internally used by `console.log(...)` / `console.error(...)`
469
+ * when ingesting logs in Oxygen production. Therefore, we want to make sure that
470
+ * the error message is as informative as possible instead of `[object Object]`.
471
+ */
472
+ toString() {
473
+ let result = `${this.name}: ${this.message}`;
474
+ if (this.path) {
475
+ try {
476
+ result += ` | path: ${JSON.stringify(this.path)}`;
477
+ } catch {
478
+ }
479
+ }
480
+ if (this.extensions) {
481
+ try {
482
+ result += ` | extensions: ${JSON.stringify(this.extensions)}`;
483
+ } catch {
484
+ }
485
+ }
486
+ result += "\n";
487
+ if (this.stack) {
488
+ result += `${this.stack.slice(this.stack.indexOf("\n") + 1)}
489
+ `;
490
+ }
491
+ return result;
492
+ }
493
+ /**
494
+ * Note: toJSON` is internally used by `JSON.stringify(...)`.
495
+ * The most common scenario when this error instance is going to be stringified is
496
+ * when it's passed to Remix' `json` and `defer` functions: e.g. `defer({promise: storefront.query(...)})`.
497
+ * In this situation, we don't want to expose private error information to the browser so we only
498
+ * do it in development.
499
+ */
500
+ toJSON() {
501
+ const formatted = { name: "Error", message: "" };
502
+ {
503
+ formatted.name = this.name;
504
+ formatted.message = "Development: " + this.message;
505
+ if (this.path)
506
+ formatted.path = this.path;
507
+ if (this.locations)
508
+ formatted.locations = this.locations;
509
+ if (this.extensions)
510
+ formatted.extensions = this.extensions;
511
+ }
512
+ return formatted;
513
+ }
514
+ };
515
+ function throwErrorWithGqlLink({
516
+ url,
517
+ response,
518
+ errors,
519
+ type,
520
+ query,
521
+ queryVariables,
522
+ ErrorConstructor = Error,
523
+ client = "storefront"
524
+ }) {
525
+ const errorMessage = (typeof errors === "string" ? errors : errors?.map?.((error) => error.message).join("\n")) || `URL: ${url}
526
+ API response error: ${response.status}`;
527
+ const gqlError = new GraphQLError(errorMessage, {
528
+ query,
529
+ queryVariables,
530
+ cause: { errors },
531
+ clientOperation: `${client}.${type}`,
532
+ requestId: response.headers.get("x-request-id")
533
+ });
534
+ throw new ErrorConstructor(gqlError.message, { cause: gqlError.cause });
535
+ }
536
+
537
+ // src/utils/callsites.ts
538
+ function withSyncStack(promise, options = {}) {
539
+ const syncError = new Error();
540
+ const getSyncStack = (message, name = "Error") => {
541
+ const syncStack = (syncError.stack ?? "").split("\n").slice(3 + (options.stackOffset ?? 0)).join("\n").replace(/ at loader(\d+) \(/, (all, m1) => all.replace(m1, ""));
542
+ return `${name}: ${message}
543
+ ` + syncStack;
544
+ };
545
+ return promise.then((result) => {
546
+ if (result?.errors && Array.isArray(result.errors)) {
547
+ const logErrors = typeof options.logErrors === "function" ? options.logErrors : () => options.logErrors ?? false;
548
+ result.errors.forEach((error) => {
549
+ if (error) {
550
+ error.stack = getSyncStack(error.message, error.name);
551
+ if (logErrors(error))
552
+ console.error(error);
553
+ }
554
+ });
555
+ }
556
+ return result;
557
+ }).catch((error) => {
558
+ if (error)
559
+ error.stack = getSyncStack(error.message, error.name);
560
+ throw error;
561
+ });
562
+ }
563
+ var getCallerStackLine = (stackOffset = 0) => {
564
+ let stackInfo = void 0;
565
+ const original = Error.prepareStackTrace;
566
+ Error.prepareStackTrace = (_, callsites) => {
567
+ const cs = callsites[2 + stackOffset];
568
+ stackInfo = cs && {
569
+ file: cs.getFileName() ?? void 0,
570
+ func: cs.getFunctionName() ?? void 0,
571
+ line: cs.getLineNumber() ?? void 0,
572
+ column: cs.getColumnNumber() ?? void 0
573
+ };
574
+ return "";
575
+ };
576
+ const err = { stack: "" };
577
+ Error.captureStackTrace(err);
578
+ err.stack;
579
+ Error.prepareStackTrace = original;
580
+ return stackInfo;
581
+ } ;
582
+
583
+ // src/storefront.ts
584
+ var defaultI18n = { language: "EN", country: "US" };
585
+ function createStorefrontClient(options) {
586
+ const {
587
+ storefrontHeaders,
588
+ cache,
589
+ waitUntil,
590
+ i18n,
591
+ storefrontId,
592
+ logErrors = true,
593
+ ...clientOptions
594
+ } = options;
595
+ const H2_PREFIX_WARN = "[h2:warn:createStorefrontClient] ";
596
+ if (!cache) {
597
+ warnOnce(
598
+ H2_PREFIX_WARN + "Storefront API client created without a cache instance. This may slow down your sub-requests."
599
+ );
600
+ }
601
+ const {
602
+ getPublicTokenHeaders,
603
+ getPrivateTokenHeaders,
604
+ getStorefrontApiUrl,
605
+ getShopifyDomain
606
+ } = createStorefrontClient$1(clientOptions);
607
+ const getHeaders = clientOptions.privateStorefrontToken ? getPrivateTokenHeaders : getPublicTokenHeaders;
608
+ const defaultHeaders = getHeaders({
609
+ contentType: "json",
610
+ buyerIp: storefrontHeaders?.buyerIp || ""
611
+ });
612
+ defaultHeaders[STOREFRONT_REQUEST_GROUP_ID_HEADER] = storefrontHeaders?.requestGroupId || generateUUID();
613
+ if (storefrontId)
614
+ defaultHeaders[SHOPIFY_STOREFRONT_ID_HEADER] = storefrontId;
615
+ defaultHeaders["user-agent"] = `Hydrogen ${LIB_VERSION}`;
616
+ if (storefrontHeaders && storefrontHeaders.cookie) {
617
+ const cookies = getShopifyCookies(storefrontHeaders.cookie ?? "");
618
+ if (cookies[SHOPIFY_Y])
619
+ defaultHeaders[SHOPIFY_STOREFRONT_Y_HEADER] = cookies[SHOPIFY_Y];
620
+ if (cookies[SHOPIFY_S])
621
+ defaultHeaders[SHOPIFY_STOREFRONT_S_HEADER] = cookies[SHOPIFY_S];
622
+ }
623
+ const cacheKeyHeader = JSON.stringify({
624
+ "content-type": defaultHeaders["content-type"],
625
+ "user-agent": defaultHeaders["user-agent"],
626
+ [SDK_VARIANT_HEADER]: defaultHeaders[SDK_VARIANT_HEADER],
627
+ [SDK_VARIANT_SOURCE_HEADER]: defaultHeaders[SDK_VARIANT_SOURCE_HEADER],
628
+ [SDK_VERSION_HEADER]: defaultHeaders[SDK_VERSION_HEADER],
629
+ [STOREFRONT_ACCESS_TOKEN_HEADER]: defaultHeaders[STOREFRONT_ACCESS_TOKEN_HEADER]
630
+ });
631
+ async function fetchStorefrontApi({
632
+ query,
633
+ mutation,
634
+ variables,
635
+ cache: cacheOptions,
636
+ headers = [],
637
+ storefrontApiVersion,
638
+ displayName,
639
+ stackInfo
640
+ }) {
641
+ const userHeaders = headers instanceof Headers ? Object.fromEntries(headers.entries()) : Array.isArray(headers) ? Object.fromEntries(headers) : headers;
642
+ query = query ?? mutation;
643
+ const queryVariables = { ...variables };
644
+ if (i18n) {
645
+ if (!variables?.country && /\$country/.test(query)) {
646
+ queryVariables.country = i18n.country;
647
+ }
648
+ if (!variables?.language && /\$language/.test(query)) {
649
+ queryVariables.language = i18n.language;
650
+ }
651
+ }
652
+ const url = getStorefrontApiUrl({ storefrontApiVersion });
653
+ const graphqlData = JSON.stringify({ query, variables: queryVariables });
654
+ const requestInit = {
655
+ method: "POST",
656
+ headers: { ...defaultHeaders, ...userHeaders },
657
+ body: graphqlData
658
+ };
659
+ const cacheKey = [
660
+ url,
661
+ requestInit.method,
662
+ cacheKeyHeader,
663
+ requestInit.body
664
+ ];
665
+ const [body, response] = await fetchWithServerCache(url, requestInit, {
666
+ cacheInstance: mutation ? void 0 : cache,
667
+ cache: cacheOptions || CacheDefault(),
668
+ cacheKey,
669
+ shouldCacheResponse: checkGraphQLErrors,
670
+ waitUntil,
671
+ debugInfo: {
672
+ url,
673
+ graphql: graphqlData,
674
+ requestId: requestInit.headers[STOREFRONT_REQUEST_GROUP_ID_HEADER],
675
+ purpose: storefrontHeaders?.purpose,
676
+ stackInfo,
677
+ displayName
678
+ }
679
+ });
680
+ const errorOptions = {
681
+ url,
682
+ response,
683
+ type: mutation ? "mutation" : "query",
684
+ query,
685
+ queryVariables,
686
+ errors: void 0
687
+ };
688
+ if (!response.ok) {
689
+ let errors2;
690
+ try {
691
+ errors2 = parseJSON(body);
692
+ } catch (_e) {
693
+ errors2 = [{ message: body }];
694
+ }
695
+ throwErrorWithGqlLink({ ...errorOptions, errors: errors2 });
696
+ }
697
+ const { data, errors } = body;
698
+ const gqlErrors = errors?.map(
699
+ ({ message, ...rest }) => new GraphQLError(message, {
700
+ ...rest,
701
+ clientOperation: `storefront.${errorOptions.type}`,
702
+ requestId: response.headers.get("x-request-id"),
703
+ queryVariables,
704
+ query
705
+ })
706
+ );
707
+ return formatAPIResult(data, gqlErrors);
708
+ }
709
+ return {
710
+ storefront: {
711
+ /**
712
+ * Sends a GraphQL query to the Storefront API.
713
+ *
714
+ * Example:
715
+ *
716
+ * ```js
717
+ * async function loader ({context: {storefront}}) {
718
+ * const data = await storefront.query('query { ... }', {
719
+ * variables: {},
720
+ * cache: storefront.CacheLong()
721
+ * });
722
+ * }
723
+ * ```
724
+ */
725
+ query(query, options2) {
726
+ query = minifyQuery(query);
727
+ assertQuery(query, "storefront.query");
728
+ const stackOffset = getStackOffset?.(query);
729
+ return withSyncStack(
730
+ fetchStorefrontApi({
731
+ ...options2,
732
+ query,
733
+ stackInfo: getCallerStackLine?.(stackOffset)
734
+ }),
735
+ { stackOffset, logErrors }
736
+ );
737
+ },
738
+ /**
739
+ * Sends a GraphQL mutation to the Storefront API.
740
+ *
741
+ * Example:
742
+ *
743
+ * ```js
744
+ * async function loader ({context: {storefront}}) {
745
+ * await storefront.mutate('mutation { ... }', {
746
+ * variables: {},
747
+ * });
748
+ * }
749
+ * ```
750
+ */
751
+ mutate(mutation, options2) {
752
+ mutation = minifyQuery(mutation);
753
+ assertMutation(mutation, "storefront.mutate");
754
+ const stackOffset = getStackOffset?.(mutation);
755
+ return withSyncStack(
756
+ fetchStorefrontApi({
757
+ ...options2,
758
+ mutation,
759
+ stackInfo: getCallerStackLine?.(stackOffset)
760
+ }),
761
+ { stackOffset, logErrors }
762
+ );
763
+ },
764
+ cache,
765
+ CacheNone,
766
+ CacheLong,
767
+ CacheShort,
768
+ CacheCustom,
769
+ generateCacheControlHeader,
770
+ getPublicTokenHeaders,
771
+ getPrivateTokenHeaders,
772
+ getShopifyDomain,
773
+ getApiUrl: getStorefrontApiUrl,
774
+ /**
775
+ * @deprecated
776
+ * Wether it's a GraphQL error returned in the Storefront API response.
777
+ *
778
+ * Example:
779
+ *
780
+ * ```js
781
+ * async function loader ({context: {storefront}}) {
782
+ * try {
783
+ * await storefront.query(...);
784
+ * } catch(error) {
785
+ * if (storefront.isApiError(error)) {
786
+ * // ...
787
+ * }
788
+ *
789
+ * throw error;
790
+ * }
791
+ * }
792
+ * ```
793
+ */
794
+ isApiError: (error) => {
795
+ {
796
+ warnOnce(
797
+ "`isApiError` is deprecated. An `errors` object would be returned from the API if there is an error."
798
+ );
799
+ }
800
+ return false;
801
+ },
802
+ i18n: i18n ?? defaultI18n
803
+ }
804
+ };
805
+ }
806
+ var getStackOffset = (query) => {
807
+ let stackOffset = 0;
808
+ if (/fragment CartApi(Query|Mutation) on Cart/.test(query)) {
809
+ stackOffset = 1;
810
+ }
811
+ return stackOffset;
812
+ } ;
813
+ function formatAPIResult(data, errors) {
814
+ return {
815
+ ...data,
816
+ ...errors && { errors }
817
+ };
818
+ }
819
+
820
+ // src/utils/request.ts
821
+ function getHeader(request, key) {
822
+ const value = request.headers?.get?.(key) ?? request.headers?.[key];
823
+ return typeof value === "string" ? value : null;
824
+ }
825
+ function getDebugHeaders(request) {
826
+ return {
827
+ requestId: request ? getHeader(request, "request-id") : void 0,
828
+ purpose: request ? getHeader(request, "purpose") : void 0
829
+ };
830
+ }
831
+
832
+ // src/with-cache.ts
833
+ function createWithCache({
834
+ cache,
835
+ waitUntil,
836
+ request
837
+ }) {
838
+ return function withCache(cacheKey, strategy, actionFn) {
839
+ return runWithCache(cacheKey, actionFn, {
840
+ strategy,
841
+ cacheInstance: cache,
842
+ waitUntil,
843
+ debugInfo: {
844
+ ...getDebugHeaders(request),
845
+ stackInfo: getCallerStackLine?.()
846
+ }
847
+ });
848
+ };
849
+ }
850
+
851
+ // src/cache/in-memory.ts
852
+ var InMemoryCache = class {
853
+ #store;
854
+ constructor() {
855
+ this.#store = /* @__PURE__ */ new Map();
856
+ }
857
+ add(request) {
858
+ throw new Error("Method not implemented. Use `put` instead.");
859
+ }
860
+ addAll(requests) {
861
+ throw new Error("Method not implemented. Use `put` instead.");
862
+ }
863
+ matchAll(request, options) {
864
+ throw new Error("Method not implemented. Use `match` instead.");
865
+ }
866
+ async put(request, response) {
867
+ if (request.method !== "GET") {
868
+ throw new TypeError("Cannot cache response to non-GET request.");
869
+ }
870
+ if (response.status === 206) {
871
+ throw new TypeError(
872
+ "Cannot cache response to a range request (206 Partial Content)."
873
+ );
874
+ }
875
+ if (response.headers.get("vary")?.includes("*")) {
876
+ throw new TypeError("Cannot cache response with 'Vary: *' header.");
877
+ }
878
+ this.#store.set(request.url, {
879
+ body: new Uint8Array(await response.arrayBuffer()),
880
+ status: response.status,
881
+ headers: [...response.headers],
882
+ timestamp: Date.now()
883
+ });
884
+ }
885
+ async match(request) {
886
+ if (request.method !== "GET")
887
+ return;
888
+ const match = this.#store.get(request.url);
889
+ if (!match) {
890
+ return;
891
+ }
892
+ const { body, timestamp, ...metadata } = match;
893
+ const headers = new Headers(metadata.headers);
894
+ const cacheControl = headers.get("cache-control") || headers.get("real-cache-control") || "";
895
+ const maxAge = parseInt(
896
+ cacheControl.match(/max-age=(\d+)/)?.[1] || "0",
897
+ 10
898
+ );
899
+ const swr = parseInt(
900
+ cacheControl.match(/stale-while-revalidate=(\d+)/)?.[1] || "0",
901
+ 10
902
+ );
903
+ const age = (Date.now() - timestamp) / 1e3;
904
+ const isMiss = age > maxAge + swr;
905
+ if (isMiss) {
906
+ this.#store.delete(request.url);
907
+ return;
908
+ }
909
+ const isStale3 = age > maxAge;
910
+ headers.set("cache", isStale3 ? "STALE" : "HIT");
911
+ headers.set("date", new Date(timestamp).toUTCString());
912
+ return new Response(body, {
913
+ status: metadata.status ?? 200,
914
+ headers
915
+ });
916
+ }
917
+ async delete(request) {
918
+ if (this.#store.has(request.url)) {
919
+ this.#store.delete(request.url);
920
+ return true;
921
+ }
922
+ return false;
923
+ }
924
+ keys(request) {
925
+ const cacheKeys = [];
926
+ for (const url of this.#store.keys()) {
927
+ if (!request || request.url === url) {
928
+ cacheKeys.push(new Request(url));
929
+ }
930
+ }
931
+ return Promise.resolve(cacheKeys);
932
+ }
933
+ };
934
+
935
+ // src/utils/get-redirect-url.ts
936
+ function getRedirectUrl(requestUrl) {
937
+ if (!requestUrl)
938
+ return;
939
+ const { pathname, search } = new URL(requestUrl);
940
+ const redirectFrom = pathname + search;
941
+ const searchParams = new URLSearchParams(search);
942
+ const redirectTo = searchParams.get("return_to") || searchParams.get("redirect");
943
+ if (redirectTo) {
944
+ if (isLocalPath(requestUrl, redirectTo)) {
945
+ return redirectTo;
946
+ } else {
947
+ console.warn(
948
+ `Cross-domain redirects are not supported. Tried to redirect from ${redirectFrom} to ${redirectTo}`
949
+ );
950
+ }
951
+ }
952
+ }
953
+ function isLocalPath(requestUrl, redirectUrl) {
954
+ try {
955
+ return new URL(requestUrl).origin === new URL(redirectUrl, requestUrl).origin;
956
+ } catch (e) {
957
+ return false;
958
+ }
959
+ }
960
+
961
+ // src/routing/redirect.ts
962
+ async function storefrontRedirect(options) {
963
+ const {
964
+ storefront,
965
+ request,
966
+ noAdminRedirect,
967
+ response = new Response("Not Found", { status: 404 })
968
+ } = options;
969
+ const url = new URL(request.url);
970
+ const isSoftNavigation = url.searchParams.has("_data");
971
+ url.searchParams.delete("_data");
972
+ const redirectFrom = url.toString().replace(url.origin, "");
973
+ if (url.pathname === "/admin" && !noAdminRedirect) {
974
+ return createRedirectResponse(
975
+ `${storefront.getShopifyDomain()}/admin`,
976
+ isSoftNavigation
977
+ );
978
+ }
979
+ try {
980
+ const { urlRedirects } = await storefront.query(REDIRECT_QUERY, {
981
+ variables: { query: "path:" + redirectFrom }
982
+ });
983
+ const location = urlRedirects?.edges?.[0]?.node?.target;
984
+ if (location) {
985
+ return createRedirectResponse(location, isSoftNavigation);
986
+ }
987
+ const redirectTo = getRedirectUrl(request.url);
988
+ if (redirectTo) {
989
+ return createRedirectResponse(redirectTo, isSoftNavigation);
990
+ }
991
+ } catch (error) {
992
+ console.error(
993
+ `Failed to fetch redirects from Storefront API for route ${redirectFrom}`,
994
+ error
995
+ );
996
+ }
997
+ return response;
998
+ }
999
+ function createRedirectResponse(location, isSoftNavigation) {
1000
+ if (isSoftNavigation) {
1001
+ return new Response(null, {
1002
+ status: 200,
1003
+ headers: { "X-Remix-Redirect": location, "X-Remix-Status": "302" }
1004
+ });
1005
+ } else {
1006
+ return new Response(null, {
1007
+ status: 302,
1008
+ headers: { location }
1009
+ });
1010
+ }
1011
+ }
1012
+ var REDIRECT_QUERY = `#graphql
1013
+ query redirects($query: String) {
1014
+ urlRedirects(first: 1, query: $query) {
1015
+ edges {
1016
+ node {
1017
+ target
1018
+ }
1019
+ }
1020
+ }
1021
+ }
1022
+ `;
1023
+
1024
+ // src/routing/graphiql.ts
1025
+ var graphiqlLoader = async function graphiqlLoader2({
1026
+ request,
1027
+ context
1028
+ }) {
1029
+ const storefront = context.storefront;
1030
+ const customerAccount = context.customerAccount;
1031
+ const url = new URL(request.url);
1032
+ if (!storefront) {
1033
+ throw new Error(
1034
+ `GraphiQL: Hydrogen's storefront client must be injected in the loader context.`
1035
+ );
1036
+ }
1037
+ const schemas = {};
1038
+ if (storefront) {
1039
+ const authHeader = "X-Shopify-Storefront-Access-Token";
1040
+ schemas.storefront = {
1041
+ name: "Storefront API",
1042
+ authHeader,
1043
+ accessToken: storefront.getPublicTokenHeaders()[authHeader],
1044
+ apiUrl: storefront.getApiUrl(),
1045
+ icon: "SF"
1046
+ };
1047
+ }
1048
+ if (customerAccount) {
1049
+ const customerAccountSchema = await (await fetch(url.origin + "/graphiql/customer-account.schema.json")).json();
1050
+ const accessToken = await customerAccount.getAccessToken();
1051
+ if (customerAccountSchema) {
1052
+ schemas["customer-account"] = {
1053
+ name: "Customer Account API",
1054
+ value: customerAccountSchema,
1055
+ authHeader: "Authorization",
1056
+ accessToken,
1057
+ apiUrl: customerAccount.getApiUrl(),
1058
+ icon: "CA"
1059
+ };
1060
+ }
1061
+ }
1062
+ const favicon = `https://avatars.githubusercontent.com/u/12972006?s=48&v=4`;
1063
+ const html = String.raw;
1064
+ return new Response(
1065
+ html`
1066
+ <!DOCTYPE html>
1067
+ <html lang="en">
1068
+ <head>
1069
+ <title>GraphiQL</title>
1070
+ <link rel="icon" type="image/x-icon" href="${favicon}" />
1071
+ <style>
1072
+ body {
1073
+ height: 100%;
1074
+ margin: 0;
1075
+ width: 100%;
1076
+ overflow: hidden;
1077
+ background-color: hsl(219, 29%, 18%);
1078
+ }
1079
+
1080
+ #graphiql {
1081
+ height: 100vh;
1082
+ }
1083
+
1084
+ #graphiql > .placeholder {
1085
+ color: slategray;
1086
+ width: fit-content;
1087
+ margin: 40px auto;
1088
+ font-family: Arial;
1089
+ }
1090
+
1091
+ .graphiql-api-toolbar-label {
1092
+ position: absolute;
1093
+ bottom: -6px;
1094
+ right: -4px;
1095
+ font-size: 8px;
1096
+ }
1097
+ </style>
1098
+
1099
+ <script
1100
+ crossorigin
1101
+ src="https://unpkg.com/react@18/umd/react.development.js"
1102
+ ></script>
1103
+ <script
1104
+ crossorigin
1105
+ src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"
1106
+ ></script>
1107
+ <link
1108
+ rel="stylesheet"
1109
+ href="https://unpkg.com/graphiql@3/graphiql.min.css"
1110
+ />
1111
+ <link
1112
+ rel="stylesheet"
1113
+ href="https://unpkg.com/@graphiql/plugin-explorer/dist/style.css"
1114
+ />
1115
+ </head>
1116
+
1117
+ <body>
1118
+ <div id="graphiql">
1119
+ <div class="placeholder">Loading GraphiQL...</div>
1120
+ </div>
1121
+
1122
+ <script
1123
+ src="https://unpkg.com/graphiql@3/graphiql.min.js"
1124
+ type="application/javascript"
1125
+ crossorigin="anonymous"
1126
+ ></script>
1127
+ <script
1128
+ src="https://unpkg.com/@graphiql/plugin-explorer/dist/index.umd.js"
1129
+ type="application/javascript"
1130
+ crossorigin="anonymous"
1131
+ ></script>
1132
+
1133
+ <script>
1134
+ const windowUrl = new URL(document.URL);
1135
+ const startingSchemaKey =
1136
+ windowUrl.searchParams.get('schema') || 'storefront';
1137
+
1138
+ let query = '{ shop { name } }';
1139
+ if (windowUrl.searchParams.has('query')) {
1140
+ query = decodeURIComponent(
1141
+ windowUrl.searchParams.get('query') ?? query,
1142
+ );
1143
+ }
1144
+
1145
+ // Prettify query
1146
+ query = GraphiQL.GraphQL.print(GraphiQL.GraphQL.parse(query));
1147
+
1148
+ let variables;
1149
+ if (windowUrl.searchParams.has('variables')) {
1150
+ variables = decodeURIComponent(
1151
+ windowUrl.searchParams.get('variables') ?? '',
1152
+ );
1153
+ }
1154
+
1155
+ // Prettify variables
1156
+ if (variables) {
1157
+ variables = JSON.stringify(JSON.parse(variables), null, 2);
1158
+ }
1159
+
1160
+ const schemas = ${JSON.stringify(schemas)};
1161
+ let lastActiveTabIndex = -1;
1162
+ let lastTabAmount = -1;
1163
+
1164
+ const root = ReactDOM.createRoot(
1165
+ document.getElementById('graphiql'),
1166
+ );
1167
+
1168
+ root.render(React.createElement(RootWrapper));
1169
+
1170
+ const TAB_STATE_KEY = 'graphiql:tabState';
1171
+ const storage = {
1172
+ getTabState: () =>
1173
+ JSON.parse(localStorage.getItem(TAB_STATE_KEY)),
1174
+ setTabState: (state) =>
1175
+ localStorage.setItem(TAB_STATE_KEY, JSON.stringify(state)),
1176
+ };
1177
+
1178
+ let nextSchemaKey;
1179
+
1180
+ function RootWrapper() {
1181
+ const [activeSchema, setActiveSchema] =
1182
+ React.useState(startingSchemaKey);
1183
+
1184
+ const schema = schemas[activeSchema];
1185
+ if (!schema) {
1186
+ throw new Error('No schema found for ' + activeSchema);
1187
+ }
1188
+
1189
+ const keys = Object.keys(schemas);
1190
+
1191
+ return React.createElement(
1192
+ GraphiQL,
1193
+ {
1194
+ fetcher: GraphiQL.createFetcher({
1195
+ url: schema.apiUrl,
1196
+ headers: {[schema.authHeader]: schema.accessToken},
1197
+ }),
1198
+ defaultEditorToolsVisibility: true,
1199
+ query,
1200
+ variables,
1201
+ schema: schema.value,
1202
+ plugins: [GraphiQLPluginExplorer.explorerPlugin()],
1203
+ onTabChange: (state) => {
1204
+ const {activeTabIndex, tabs} = state;
1205
+ const activeTab = tabs[activeTabIndex];
1206
+
1207
+ if (
1208
+ activeTabIndex === lastActiveTabIndex &&
1209
+ lastTabAmount === tabs.length
1210
+ ) {
1211
+ if (
1212
+ nextSchemaKey &&
1213
+ activeTab &&
1214
+ activeTab.schemaKey !== nextSchemaKey
1215
+ ) {
1216
+ activeTab.schemaKey = nextSchemaKey;
1217
+ nextSchemaKey = undefined;
1218
+
1219
+ // Sync state to localStorage. GraphiQL resets the state
1220
+ // asynchronously, so we need to do it in a timeout.
1221
+ storage.setTabState(state);
1222
+ setTimeout(() => storage.setTabState(state), 500);
1223
+ }
1224
+
1225
+ // React rerrendering, skip
1226
+ return;
1227
+ }
1228
+
1229
+ if (activeTab) {
1230
+ if (!activeTab.schemaKey) {
1231
+ // Creating a new tab
1232
+ if (lastTabAmount < tabs.length) {
1233
+ activeTab.schemaKey = activeSchema;
1234
+ storage.setTabState(state);
1235
+ }
1236
+ }
1237
+
1238
+ const nextSchema = activeTab.schemaKey || 'storefront';
1239
+
1240
+ if (nextSchema !== activeSchema) {
1241
+ setActiveSchema(nextSchema);
1242
+ }
1243
+ }
1244
+
1245
+ lastActiveTabIndex = activeTabIndex;
1246
+ lastTabAmount = tabs.length;
1247
+ },
1248
+ toolbar: {
1249
+ additionalComponent: function () {
1250
+ const schema = schemas[activeSchema];
1251
+
1252
+ return React.createElement(
1253
+ GraphiQL.React.ToolbarButton,
1254
+ {
1255
+ onClick: () => {
1256
+ const activeKeyIndex = keys.indexOf(activeSchema);
1257
+ nextSchemaKey =
1258
+ keys[(activeKeyIndex + 1) % keys.length];
1259
+
1260
+ // This triggers onTabChange
1261
+ if (nextSchemaKey) setActiveSchema(nextSchemaKey);
1262
+ },
1263
+ label: 'Toggle between different API schemas',
1264
+ },
1265
+ React.createElement(
1266
+ 'div',
1267
+ {
1268
+ key: 'api-wrapper',
1269
+ className: 'graphiql-toolbar-icon',
1270
+ style: {position: 'relative', fontWeight: 'bolder'},
1271
+ },
1272
+ [
1273
+ React.createElement(
1274
+ 'div',
1275
+ {key: 'icon', style: {textAlign: 'center'}},
1276
+ [
1277
+ schema.icon,
1278
+ React.createElement(
1279
+ 'div',
1280
+ {
1281
+ key: 'icon-label',
1282
+ className: 'graphiql-api-toolbar-label',
1283
+ },
1284
+ 'API',
1285
+ ),
1286
+ ],
1287
+ ),
1288
+ ],
1289
+ ),
1290
+ );
1291
+ },
1292
+ },
1293
+ },
1294
+ [
1295
+ React.createElement(
1296
+ GraphiQL.Logo,
1297
+ {
1298
+ key: 'Logo replacement',
1299
+ },
1300
+ [
1301
+ React.createElement(
1302
+ 'div',
1303
+ {
1304
+ key: 'Logo wrapper',
1305
+ style: {display: 'flex', alignItems: 'center'},
1306
+ },
1307
+ [
1308
+ React.createElement(
1309
+ 'div',
1310
+ {
1311
+ key: 'api',
1312
+ className: 'graphiql-logo',
1313
+ style: {
1314
+ paddingRight: 0,
1315
+ whiteSpace: 'nowrap',
1316
+ },
1317
+ },
1318
+ [schema.name],
1319
+ ),
1320
+ React.createElement(GraphiQL.Logo, {key: 'logo'}),
1321
+ ],
1322
+ ),
1323
+ ],
1324
+ ),
1325
+ ],
1326
+ );
1327
+ }
1328
+ </script>
1329
+ </body>
1330
+ </html>
1331
+ `,
1332
+ { status: 200, headers: { "content-type": "text/html" } }
1333
+ );
1334
+ };
1335
+
1336
+ // src/seo/escape.ts
1337
+ var ESCAPE_LOOKUP = {
1338
+ "&": "\\u0026",
1339
+ ">": "\\u003e",
1340
+ "<": "\\u003c",
1341
+ "\u2028": "\\u2028",
1342
+ "\u2029": "\\u2029"
1343
+ };
1344
+ var ESCAPE_REGEX = /[&><\u2028\u2029]/g;
1345
+ function escapeHtml(html) {
1346
+ return html.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
1347
+ }
1348
+
1349
+ // src/seo/generate-seo-tags.ts
1350
+ var ERROR_PREFIX = "Error in SEO input: ";
1351
+ var schema = {
1352
+ title: {
1353
+ validate: (value) => {
1354
+ if (typeof value !== "string") {
1355
+ throw new Error(ERROR_PREFIX.concat("`title` should be a string"));
1356
+ }
1357
+ if (typeof value === "string" && value.length > 120) {
1358
+ throw new Error(
1359
+ ERROR_PREFIX.concat(
1360
+ "`title` should not be longer than 120 characters"
1361
+ )
1362
+ );
1363
+ }
1364
+ return value;
1365
+ }
1366
+ },
1367
+ description: {
1368
+ validate: (value) => {
1369
+ if (typeof value !== "string") {
1370
+ throw new Error(
1371
+ ERROR_PREFIX.concat("`description` should be a string")
1372
+ );
1373
+ }
1374
+ if (typeof value === "string" && value.length > 155) {
1375
+ throw new Error(
1376
+ ERROR_PREFIX.concat(
1377
+ "`description` should not be longer than 155 characters"
1378
+ )
1379
+ );
1380
+ }
1381
+ return value;
1382
+ }
1383
+ },
1384
+ url: {
1385
+ validate: (value) => {
1386
+ if (typeof value !== "string") {
1387
+ throw new Error(ERROR_PREFIX.concat("`url` should be a string"));
1388
+ }
1389
+ if (typeof value === "string" && !value.startsWith("http")) {
1390
+ throw new Error(ERROR_PREFIX.concat("`url` should be a valid URL"));
1391
+ }
1392
+ return value;
1393
+ }
1394
+ },
1395
+ handle: {
1396
+ validate: (value) => {
1397
+ if (typeof value !== "string") {
1398
+ throw new Error(ERROR_PREFIX.concat("`handle` should be a string"));
1399
+ }
1400
+ if (typeof value === "string" && !value.startsWith("@")) {
1401
+ throw new Error(ERROR_PREFIX.concat("`handle` should start with `@`"));
1402
+ }
1403
+ return value;
1404
+ }
1405
+ }
1406
+ };
1407
+ function generateSeoTags(seoInput) {
1408
+ const tagResults = [];
1409
+ for (const seoKey of Object.keys(seoInput)) {
1410
+ switch (seoKey) {
1411
+ case "title": {
1412
+ const content = validate(schema.title, seoInput.title);
1413
+ const title = renderTitle(seoInput?.titleTemplate, content);
1414
+ if (!title) {
1415
+ break;
1416
+ }
1417
+ tagResults.push(
1418
+ generateTag("title", { title }),
1419
+ generateTag("meta", { property: "og:title", content: title }),
1420
+ generateTag("meta", { name: "twitter:title", content: title })
1421
+ );
1422
+ break;
1423
+ }
1424
+ case "description": {
1425
+ const content = validate(schema.description, seoInput.description);
1426
+ if (!content) {
1427
+ break;
1428
+ }
1429
+ tagResults.push(
1430
+ generateTag("meta", {
1431
+ name: "description",
1432
+ content
1433
+ }),
1434
+ generateTag("meta", {
1435
+ property: "og:description",
1436
+ content
1437
+ }),
1438
+ generateTag("meta", {
1439
+ name: "twitter:description",
1440
+ content
1441
+ })
1442
+ );
1443
+ break;
1444
+ }
1445
+ case "url": {
1446
+ const content = validate(schema.url, seoInput.url);
1447
+ if (!content) {
1448
+ break;
1449
+ }
1450
+ const urlWithoutParams = content.split("?")[0];
1451
+ const urlWithoutTrailingSlash = urlWithoutParams.replace(/\/$/, "");
1452
+ tagResults.push(
1453
+ generateTag("link", {
1454
+ rel: "canonical",
1455
+ href: urlWithoutTrailingSlash
1456
+ }),
1457
+ generateTag("meta", {
1458
+ property: "og:url",
1459
+ content: urlWithoutTrailingSlash
1460
+ })
1461
+ );
1462
+ break;
1463
+ }
1464
+ case "handle": {
1465
+ const content = validate(schema.handle, seoInput.handle);
1466
+ if (!content) {
1467
+ break;
1468
+ }
1469
+ tagResults.push(
1470
+ generateTag("meta", { name: "twitter:site", content }),
1471
+ generateTag("meta", { name: "twitter:creator", content })
1472
+ );
1473
+ break;
1474
+ }
1475
+ case "media": {
1476
+ let content;
1477
+ const values = ensureArray(seoInput.media);
1478
+ for (const media of values) {
1479
+ if (typeof media === "string") {
1480
+ tagResults.push(
1481
+ generateTag("meta", { name: "og:image", content: media })
1482
+ );
1483
+ }
1484
+ if (media && typeof media === "object") {
1485
+ const type = media.type || "image";
1486
+ const normalizedMedia = media ? {
1487
+ url: media?.url,
1488
+ secure_url: media?.url,
1489
+ type: inferMimeType(media.url),
1490
+ width: media?.width,
1491
+ height: media?.height,
1492
+ alt: media?.altText
1493
+ } : {};
1494
+ for (const key of Object.keys(normalizedMedia)) {
1495
+ if (normalizedMedia[key]) {
1496
+ content = normalizedMedia[key];
1497
+ tagResults.push(
1498
+ generateTag(
1499
+ "meta",
1500
+ {
1501
+ property: `og:${type}:${key}`,
1502
+ content
1503
+ },
1504
+ normalizedMedia.url
1505
+ )
1506
+ );
1507
+ }
1508
+ }
1509
+ }
1510
+ }
1511
+ break;
1512
+ }
1513
+ case "jsonLd": {
1514
+ const jsonLdBlocks = ensureArray(seoInput.jsonLd);
1515
+ let index = 0;
1516
+ for (const block of jsonLdBlocks) {
1517
+ if (typeof block !== "object") {
1518
+ continue;
1519
+ }
1520
+ const tag = generateTag(
1521
+ "script",
1522
+ {
1523
+ type: "application/ld+json",
1524
+ children: JSON.stringify(block, (k, value) => {
1525
+ return typeof value === "string" ? escapeHtml(value) : value;
1526
+ })
1527
+ },
1528
+ // @ts-expect-error
1529
+ `json-ld-${block?.["@type"] || block?.name || index++}`
1530
+ );
1531
+ tagResults.push(tag);
1532
+ }
1533
+ break;
1534
+ }
1535
+ case "alternates": {
1536
+ const alternates = ensureArray(seoInput.alternates);
1537
+ for (const alternate of alternates) {
1538
+ if (!alternate) {
1539
+ continue;
1540
+ }
1541
+ const { language, url, default: defaultLang } = alternate;
1542
+ const hrefLang = language ? `${language}${defaultLang ? "-default" : ""}` : void 0;
1543
+ tagResults.push(
1544
+ generateTag("link", {
1545
+ rel: "alternate",
1546
+ hrefLang,
1547
+ href: url
1548
+ })
1549
+ );
1550
+ }
1551
+ break;
1552
+ }
1553
+ case "robots": {
1554
+ if (!seoInput.robots) {
1555
+ break;
1556
+ }
1557
+ const {
1558
+ maxImagePreview,
1559
+ maxSnippet,
1560
+ maxVideoPreview,
1561
+ noArchive,
1562
+ noFollow,
1563
+ noImageIndex,
1564
+ noIndex,
1565
+ noSnippet,
1566
+ noTranslate,
1567
+ unavailableAfter
1568
+ } = seoInput.robots;
1569
+ const robotsParams = [
1570
+ noArchive && "noarchive",
1571
+ noImageIndex && "noimageindex",
1572
+ noSnippet && "nosnippet",
1573
+ noTranslate && `notranslate`,
1574
+ maxImagePreview && `max-image-preview:${maxImagePreview}`,
1575
+ maxSnippet && `max-snippet:${maxSnippet}`,
1576
+ maxVideoPreview && `max-video-preview:${maxVideoPreview}`,
1577
+ unavailableAfter && `unavailable_after:${unavailableAfter}`
1578
+ ];
1579
+ let robotsParam = (noIndex ? "noindex" : "index") + "," + (noFollow ? "nofollow" : "follow");
1580
+ for (let param of robotsParams) {
1581
+ if (param) {
1582
+ robotsParam += `,${param}`;
1583
+ }
1584
+ }
1585
+ tagResults.push(
1586
+ generateTag("meta", { name: "robots", content: robotsParam })
1587
+ );
1588
+ break;
1589
+ }
1590
+ }
1591
+ }
1592
+ return tagResults.flat().sort((a, b) => a.key.localeCompare(b.key));
1593
+ }
1594
+ function generateTag(tagName, input, group) {
1595
+ const tag = { tag: tagName, props: {}, key: "" };
1596
+ if (tagName === "title") {
1597
+ tag.children = input.title;
1598
+ tag.key = generateKey(tag);
1599
+ return tag;
1600
+ }
1601
+ if (tagName === "script") {
1602
+ tag.children = typeof input.children === "string" ? input.children : "";
1603
+ tag.key = generateKey(tag, group);
1604
+ delete input.children;
1605
+ tag.props = input;
1606
+ return tag;
1607
+ }
1608
+ tag.props = input;
1609
+ Object.keys(tag.props).forEach(
1610
+ (key) => !tag.props[key] && delete tag.props[key]
1611
+ );
1612
+ tag.key = generateKey(tag, group);
1613
+ return tag;
1614
+ }
1615
+ function generateKey(tag, group) {
1616
+ const { tag: tagName, props } = tag;
1617
+ if (tagName === "title") {
1618
+ return "0-title";
1619
+ }
1620
+ if (tagName === "meta") {
1621
+ const priority = props.content === group && typeof props.property === "string" && !props.property.endsWith("secure_url") && "0";
1622
+ const groupName = [group, priority];
1623
+ return [tagName, ...groupName, props.property || props.name].filter((x) => x).join("-");
1624
+ }
1625
+ if (tagName === "link") {
1626
+ const key = [tagName, props.rel, props.hrefLang || props.media].filter((x) => x).join("-");
1627
+ return key.replace(/\s+/g, "-");
1628
+ }
1629
+ if (tagName === "script") {
1630
+ return `${tagName}-${group}`;
1631
+ }
1632
+ return `${tagName}-${props.type}`;
1633
+ }
1634
+ function renderTitle(template, title) {
1635
+ if (!title) {
1636
+ return void 0;
1637
+ }
1638
+ if (!template) {
1639
+ return title;
1640
+ }
1641
+ if (typeof template === "function") {
1642
+ return template(title);
1643
+ }
1644
+ return template.replace("%s", title ?? "");
1645
+ }
1646
+ function inferMimeType(url) {
1647
+ const ext = url && url.split(".").pop();
1648
+ switch (ext) {
1649
+ case "svg":
1650
+ return "image/svg+xml";
1651
+ case "png":
1652
+ return "image/png";
1653
+ case "gif":
1654
+ return "image/gif";
1655
+ case "swf":
1656
+ return "application/x-shockwave-flash";
1657
+ case "mp3":
1658
+ return "audio/mpeg";
1659
+ case "jpg":
1660
+ case "jpeg":
1661
+ default:
1662
+ return "image/jpeg";
1663
+ }
1664
+ }
1665
+ function ensureArray(value) {
1666
+ return Array.isArray(value) ? value : [value];
1667
+ }
1668
+ function validate(schema2, data) {
1669
+ try {
1670
+ return schema2.validate(data);
1671
+ } catch (error) {
1672
+ console.warn(error.message);
1673
+ return data;
1674
+ }
1675
+ }
1676
+
1677
+ // src/seo/seo.ts
1678
+ var SeoLogger = lazy(() => import('./log-seo-tags-IG37ONQ2.js'));
1679
+ function Seo({ debug }) {
1680
+ const matches = useMatches();
1681
+ const location = useLocation();
1682
+ const seoConfig = useMemo(() => {
1683
+ return matches.flatMap((match) => {
1684
+ const { handle, ...routeMatch } = match;
1685
+ const routeData = { ...routeMatch, ...location };
1686
+ const handleSeo = handle?.seo;
1687
+ const loaderSeo = routeMatch?.data?.seo;
1688
+ if (!handleSeo && !loaderSeo) {
1689
+ return [];
1690
+ }
1691
+ if (handleSeo) {
1692
+ return recursivelyInvokeOrReturn(handleSeo, routeData);
1693
+ } else {
1694
+ return [loaderSeo];
1695
+ }
1696
+ }).reduce((acc, current) => {
1697
+ Object.keys(current).forEach(
1698
+ (key) => !current[key] && delete current[key]
1699
+ );
1700
+ const { jsonLd } = current;
1701
+ if (!jsonLd) {
1702
+ return { ...acc, ...current };
1703
+ }
1704
+ if (!acc?.jsonLd) {
1705
+ return { ...acc, ...current, jsonLd: [jsonLd] };
1706
+ } else {
1707
+ if (Array.isArray(jsonLd)) {
1708
+ return {
1709
+ ...acc,
1710
+ ...current,
1711
+ jsonLd: [...acc.jsonLd, ...jsonLd]
1712
+ };
1713
+ } else {
1714
+ return {
1715
+ ...acc,
1716
+ ...current,
1717
+ jsonLd: [...acc.jsonLd, jsonLd]
1718
+ };
1719
+ }
1720
+ }
1721
+ }, {});
1722
+ }, [matches, location]);
1723
+ const { html, loggerMarkup } = useMemo(() => {
1724
+ const headTags = generateSeoTags(seoConfig);
1725
+ const html2 = headTags.map((tag) => {
1726
+ if (tag.tag === "script") {
1727
+ return createElement(tag.tag, {
1728
+ ...tag.props,
1729
+ key: tag.key,
1730
+ dangerouslySetInnerHTML: { __html: tag.children }
1731
+ });
1732
+ }
1733
+ return createElement(tag.tag, { ...tag.props, key: tag.key }, tag.children);
1734
+ });
1735
+ const loggerMarkup2 = createElement(
1736
+ Suspense,
1737
+ { fallback: null },
1738
+ createElement(SeoLogger, { headTags })
1739
+ );
1740
+ return { html: html2, loggerMarkup: loggerMarkup2 };
1741
+ }, [seoConfig]);
1742
+ return createElement(Fragment, null, html, debug && loggerMarkup);
1743
+ }
1744
+ function recursivelyInvokeOrReturn(value, ...rest) {
1745
+ if (value instanceof Function) {
1746
+ return recursivelyInvokeOrReturn(value(...rest), ...rest);
1747
+ }
1748
+ let result = {};
1749
+ if (Array.isArray(value)) {
1750
+ result = value.reduce((acc, item) => {
1751
+ return [...acc, recursivelyInvokeOrReturn(item)];
1752
+ }, []);
1753
+ return result;
1754
+ }
1755
+ if (value instanceof Object) {
1756
+ const entries = Object.entries(value);
1757
+ entries.forEach(([key, val]) => {
1758
+ result[key] = recursivelyInvokeOrReturn(val, ...rest);
1759
+ });
1760
+ return result;
1761
+ }
1762
+ return value;
1763
+ }
1764
+ function Pagination({
1765
+ connection,
1766
+ children = () => {
1767
+ console.warn("<Pagination> requires children to work properly");
1768
+ return null;
1769
+ }
1770
+ }) {
1771
+ const transition = useNavigation();
1772
+ const isLoading = transition.state === "loading";
1773
+ const {
1774
+ endCursor,
1775
+ hasNextPage,
1776
+ hasPreviousPage,
1777
+ nextPageUrl,
1778
+ nodes,
1779
+ previousPageUrl,
1780
+ startCursor
1781
+ } = usePagination(connection);
1782
+ const state = useMemo(
1783
+ () => ({
1784
+ pageInfo: {
1785
+ endCursor,
1786
+ hasPreviousPage,
1787
+ hasNextPage,
1788
+ startCursor
1789
+ },
1790
+ nodes
1791
+ }),
1792
+ [endCursor, hasNextPage, hasPreviousPage, startCursor, nodes]
1793
+ );
1794
+ const NextLink = useMemo(
1795
+ () => forwardRef(function NextLink2(props, ref) {
1796
+ return hasNextPage ? createElement(Link, {
1797
+ preventScrollReset: true,
1798
+ ...props,
1799
+ to: nextPageUrl,
1800
+ state,
1801
+ replace: true,
1802
+ ref
1803
+ }) : null;
1804
+ }),
1805
+ [hasNextPage, nextPageUrl, state]
1806
+ );
1807
+ const PreviousLink = useMemo(
1808
+ () => forwardRef(function PrevLink(props, ref) {
1809
+ return hasPreviousPage ? createElement(Link, {
1810
+ preventScrollReset: true,
1811
+ ...props,
1812
+ to: previousPageUrl,
1813
+ state,
1814
+ replace: true,
1815
+ ref
1816
+ }) : null;
1817
+ }),
1818
+ [hasPreviousPage, previousPageUrl, state]
1819
+ );
1820
+ return children({
1821
+ state,
1822
+ hasNextPage,
1823
+ hasPreviousPage,
1824
+ isLoading,
1825
+ nextPageUrl,
1826
+ nodes,
1827
+ previousPageUrl,
1828
+ NextLink,
1829
+ PreviousLink
1830
+ });
1831
+ }
1832
+ function getParamsWithoutPagination(paramsString) {
1833
+ const params = new URLSearchParams(paramsString);
1834
+ params.delete("cursor");
1835
+ params.delete("direction");
1836
+ return params.toString();
1837
+ }
1838
+ function makeError(prop) {
1839
+ throw new Error(
1840
+ `The Pagination component requires ${"`" + prop + "`"} to be a part of your query. See the guide on how to setup your query to include ${"`" + prop + "`"}: https://shopify.dev/docs/custom-storefronts/hydrogen/data-fetching/pagination#setup-the-paginated-query`
1841
+ );
1842
+ }
1843
+ function usePagination(connection) {
1844
+ if (!connection.pageInfo) {
1845
+ makeError("pageInfo");
1846
+ }
1847
+ if (typeof connection.pageInfo.startCursor === "undefined") {
1848
+ makeError("pageInfo.startCursor");
1849
+ }
1850
+ if (typeof connection.pageInfo.endCursor === "undefined") {
1851
+ makeError("pageInfo.endCursor");
1852
+ }
1853
+ if (typeof connection.pageInfo.hasNextPage === "undefined") {
1854
+ makeError("pageInfo.hasNextPage");
1855
+ }
1856
+ if (typeof connection.pageInfo.hasPreviousPage === "undefined") {
1857
+ makeError("pageInfo.hasPreviousPage");
1858
+ }
1859
+ const navigate = useNavigate();
1860
+ const { state, search, pathname } = useLocation();
1861
+ const params = new URLSearchParams(search);
1862
+ const direction = params.get("direction");
1863
+ const isPrevious = direction === "previous";
1864
+ const nodes = useMemo(() => {
1865
+ if (!globalThis?.window?.__hydrogenHydrated || !state || !state?.nodes) {
1866
+ return flattenConnection(connection);
1867
+ }
1868
+ if (isPrevious) {
1869
+ return [...flattenConnection(connection), ...state.nodes];
1870
+ } else {
1871
+ return [...state.nodes, ...flattenConnection(connection)];
1872
+ }
1873
+ }, [state, connection]);
1874
+ const currentPageInfo = useMemo(() => {
1875
+ const hydrogenHydrated = globalThis?.window?.__hydrogenHydrated;
1876
+ let pageStartCursor = !hydrogenHydrated || state?.pageInfo?.startCursor === void 0 ? connection.pageInfo.startCursor : state.pageInfo.startCursor;
1877
+ let pageEndCursor = !hydrogenHydrated || state?.pageInfo?.endCursor === void 0 ? connection.pageInfo.endCursor : state.pageInfo.endCursor;
1878
+ let previousPageExists = !hydrogenHydrated || state?.pageInfo?.hasPreviousPage === void 0 ? connection.pageInfo.hasPreviousPage : state.pageInfo.hasPreviousPage;
1879
+ let nextPageExists = !hydrogenHydrated || state?.pageInfo?.hasNextPage === void 0 ? connection.pageInfo.hasNextPage : state.pageInfo.hasNextPage;
1880
+ if (state?.nodes) {
1881
+ if (isPrevious) {
1882
+ pageStartCursor = connection.pageInfo.startCursor;
1883
+ previousPageExists = connection.pageInfo.hasPreviousPage;
1884
+ } else {
1885
+ pageEndCursor = connection.pageInfo.endCursor;
1886
+ nextPageExists = connection.pageInfo.hasNextPage;
1887
+ }
1888
+ }
1889
+ return {
1890
+ startCursor: pageStartCursor,
1891
+ endCursor: pageEndCursor,
1892
+ hasPreviousPage: previousPageExists,
1893
+ hasNextPage: nextPageExists
1894
+ };
1895
+ }, [
1896
+ isPrevious,
1897
+ state,
1898
+ connection.pageInfo.hasNextPage,
1899
+ connection.pageInfo.hasPreviousPage,
1900
+ connection.pageInfo.startCursor,
1901
+ connection.pageInfo.endCursor
1902
+ ]);
1903
+ const urlRef = useRef({
1904
+ params: getParamsWithoutPagination(search),
1905
+ pathname
1906
+ });
1907
+ useEffect(() => {
1908
+ window.__hydrogenHydrated = true;
1909
+ }, []);
1910
+ useEffect(() => {
1911
+ if (
1912
+ // If the URL changes (independent of pagination params)
1913
+ // then reset the pagination params in the URL
1914
+ getParamsWithoutPagination(search) !== urlRef.current.params || pathname !== urlRef.current.pathname
1915
+ ) {
1916
+ urlRef.current = {
1917
+ pathname,
1918
+ params: getParamsWithoutPagination(search)
1919
+ };
1920
+ navigate(`${pathname}?${getParamsWithoutPagination(search)}`, {
1921
+ replace: true,
1922
+ preventScrollReset: true,
1923
+ state: { nodes: void 0, pageInfo: void 0 }
1924
+ });
1925
+ }
1926
+ }, [pathname, search]);
1927
+ const previousPageUrl = useMemo(() => {
1928
+ const params2 = new URLSearchParams(search);
1929
+ params2.set("direction", "previous");
1930
+ currentPageInfo.startCursor && params2.set("cursor", currentPageInfo.startCursor);
1931
+ return `?${params2.toString()}`;
1932
+ }, [search, currentPageInfo.startCursor]);
1933
+ const nextPageUrl = useMemo(() => {
1934
+ const params2 = new URLSearchParams(search);
1935
+ params2.set("direction", "next");
1936
+ currentPageInfo.endCursor && params2.set("cursor", currentPageInfo.endCursor);
1937
+ return `?${params2.toString()}`;
1938
+ }, [search, currentPageInfo.endCursor]);
1939
+ return { ...currentPageInfo, previousPageUrl, nextPageUrl, nodes };
1940
+ }
1941
+ function getPaginationVariables(request, options = { pageBy: 20 }) {
1942
+ if (typeof request?.url === "undefined") {
1943
+ throw new Error(
1944
+ "getPaginationVariables must be called with the Request object passed to your loader function"
1945
+ );
1946
+ }
1947
+ const { pageBy } = options;
1948
+ const searchParams = new URLSearchParams(new URL(request.url).search);
1949
+ const cursor = searchParams.get("cursor") ?? void 0;
1950
+ const direction = searchParams.get("direction") === "previous" ? "previous" : "next";
1951
+ const isPrevious = direction === "previous";
1952
+ const prevPage = {
1953
+ last: pageBy,
1954
+ startCursor: cursor ?? null
1955
+ };
1956
+ const nextPage = {
1957
+ first: pageBy,
1958
+ endCursor: cursor ?? null
1959
+ };
1960
+ const variables = isPrevious ? prevPage : nextPage;
1961
+ return variables;
1962
+ }
1963
+
1964
+ // src/customer/constants.ts
1965
+ var DEFAULT_CUSTOMER_API_VERSION = "2024-01";
1966
+ var USER_AGENT = `Shopify Hydrogen ${LIB_VERSION}`;
1967
+ var CUSTOMER_API_CLIENT_ID = "30243aa5-17c1-465a-8493-944bcc4e88aa";
1968
+ var CUSTOMER_ACCOUNT_SESSION_KEY = "customerAccount";
1969
+
1970
+ // src/customer/BadRequest.ts
1971
+ var BadRequest = class extends Response {
1972
+ constructor(message, helpMessage, headers) {
1973
+ if (helpMessage && true) {
1974
+ console.error("Customer Account API Error: " + helpMessage);
1975
+ }
1976
+ super(`Bad request: ${message}`, { status: 400, headers });
1977
+ }
1978
+ };
1979
+
1980
+ // src/customer/auth.helpers.ts
1981
+ var logSubRequestEvent = ({
1982
+ url,
1983
+ response,
1984
+ startTime,
1985
+ query,
1986
+ variables,
1987
+ ...debugInfo
1988
+ }) => {
1989
+ globalThis.__H2O_LOG_EVENT?.({
1990
+ ...debugInfo,
1991
+ eventType: "subrequest",
1992
+ url,
1993
+ startTime,
1994
+ graphql: query ? JSON.stringify({ query, variables, schema: "customer-account" }) : void 0,
1995
+ responseInit: {
1996
+ status: response.status || 0,
1997
+ statusText: response.statusText || "",
1998
+ headers: Array.from(response.headers.entries() || [])
1999
+ }
2000
+ });
2001
+ } ;
2002
+ function redirect(path, options = {}) {
2003
+ const headers = options.headers ? new Headers(options.headers) : new Headers({});
2004
+ headers.set("location", path);
2005
+ return new Response(null, { status: options.status || 302, headers });
2006
+ }
2007
+ async function refreshToken({
2008
+ session,
2009
+ customerAccountId,
2010
+ customerAccountUrl,
2011
+ origin,
2012
+ debugInfo
2013
+ }) {
2014
+ const newBody = new URLSearchParams();
2015
+ const customerAccount = session.get(CUSTOMER_ACCOUNT_SESSION_KEY);
2016
+ const refreshToken2 = customerAccount?.refreshToken;
2017
+ if (!refreshToken2)
2018
+ throw new BadRequest(
2019
+ "Unauthorized",
2020
+ "No refreshToken found in the session. Make sure your session is configured correctly and passed to `createCustomerAccountClient`."
2021
+ );
2022
+ newBody.append("grant_type", "refresh_token");
2023
+ newBody.append("refresh_token", refreshToken2);
2024
+ newBody.append("client_id", customerAccountId);
2025
+ const headers = {
2026
+ "content-type": "application/x-www-form-urlencoded",
2027
+ "User-Agent": USER_AGENT,
2028
+ Origin: origin
2029
+ };
2030
+ const startTime = (/* @__PURE__ */ new Date()).getTime();
2031
+ const url = `${customerAccountUrl}/auth/oauth/token`;
2032
+ const response = await fetch(url, {
2033
+ method: "POST",
2034
+ headers,
2035
+ body: newBody
2036
+ });
2037
+ logSubRequestEvent?.({
2038
+ displayName: "Customer Account API: access token refresh",
2039
+ url,
2040
+ startTime,
2041
+ response,
2042
+ ...debugInfo
2043
+ });
2044
+ if (!response.ok) {
2045
+ const text = await response.text();
2046
+ throw new Response(text, {
2047
+ status: response.status,
2048
+ headers: {
2049
+ "Content-Type": "text/html; charset=utf-8"
2050
+ }
2051
+ });
2052
+ }
2053
+ const { access_token, expires_in, id_token, refresh_token } = await response.json();
2054
+ const accessToken = await exchangeAccessToken(
2055
+ access_token,
2056
+ customerAccountId,
2057
+ customerAccountUrl,
2058
+ origin,
2059
+ debugInfo
2060
+ );
2061
+ session.set(CUSTOMER_ACCOUNT_SESSION_KEY, {
2062
+ accessToken,
2063
+ // Store the date in future the token expires, separated by two minutes
2064
+ expiresAt: new Date((/* @__PURE__ */ new Date()).getTime() + (expires_in - 120) * 1e3).getTime() + "",
2065
+ refreshToken: refresh_token,
2066
+ idToken: id_token
2067
+ });
2068
+ }
2069
+ function clearSession(session) {
2070
+ session.unset(CUSTOMER_ACCOUNT_SESSION_KEY);
2071
+ }
2072
+ async function checkExpires({
2073
+ locks,
2074
+ expiresAt,
2075
+ session,
2076
+ customerAccountId,
2077
+ customerAccountUrl,
2078
+ origin,
2079
+ debugInfo
2080
+ }) {
2081
+ if (parseInt(expiresAt, 10) - 1e3 < (/* @__PURE__ */ new Date()).getTime()) {
2082
+ try {
2083
+ if (!locks.refresh)
2084
+ locks.refresh = refreshToken({
2085
+ session,
2086
+ customerAccountId,
2087
+ customerAccountUrl,
2088
+ origin,
2089
+ debugInfo
2090
+ });
2091
+ await locks.refresh;
2092
+ delete locks.refresh;
2093
+ } catch (error) {
2094
+ clearSession(session);
2095
+ if (error && error.status !== 401) {
2096
+ throw error;
2097
+ } else {
2098
+ throw new BadRequest(
2099
+ "Unauthorized",
2100
+ "Login before querying the Customer Account API.",
2101
+ {
2102
+ "Set-Cookie": await session.commit()
2103
+ }
2104
+ );
2105
+ }
2106
+ }
2107
+ }
2108
+ }
2109
+ async function generateCodeVerifier() {
2110
+ const rando = generateRandomCode();
2111
+ return base64UrlEncode(rando);
2112
+ }
2113
+ async function generateCodeChallenge(codeVerifier) {
2114
+ const digestOp = await crypto.subtle.digest(
2115
+ { name: "SHA-256" },
2116
+ new TextEncoder().encode(codeVerifier)
2117
+ );
2118
+ const hash = convertBufferToString(digestOp);
2119
+ return base64UrlEncode(hash);
2120
+ }
2121
+ function generateRandomCode() {
2122
+ const array = new Uint8Array(32);
2123
+ crypto.getRandomValues(array);
2124
+ return String.fromCharCode.apply(null, Array.from(array));
2125
+ }
2126
+ function base64UrlEncode(str) {
2127
+ const base64 = btoa(str);
2128
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
2129
+ }
2130
+ function convertBufferToString(hash) {
2131
+ const uintArray = new Uint8Array(hash);
2132
+ const numberArray = Array.from(uintArray);
2133
+ return String.fromCharCode(...numberArray);
2134
+ }
2135
+ async function generateState() {
2136
+ const timestamp = Date.now().toString();
2137
+ const randomString = Math.random().toString(36).substring(2);
2138
+ return timestamp + randomString;
2139
+ }
2140
+ async function exchangeAccessToken(authAccessToken, customerAccountId, customerAccountUrl, origin, debugInfo) {
2141
+ const clientId = customerAccountId;
2142
+ if (!authAccessToken)
2143
+ throw new BadRequest(
2144
+ "Unauthorized",
2145
+ "oAuth access token was not provided during token exchange."
2146
+ );
2147
+ const body = new URLSearchParams();
2148
+ body.append("grant_type", "urn:ietf:params:oauth:grant-type:token-exchange");
2149
+ body.append("client_id", clientId);
2150
+ body.append("audience", CUSTOMER_API_CLIENT_ID);
2151
+ body.append("subject_token", authAccessToken);
2152
+ body.append(
2153
+ "subject_token_type",
2154
+ "urn:ietf:params:oauth:token-type:access_token"
2155
+ );
2156
+ body.append("scopes", "https://api.customers.com/auth/customer.graphql");
2157
+ const headers = {
2158
+ "content-type": "application/x-www-form-urlencoded",
2159
+ "User-Agent": USER_AGENT,
2160
+ Origin: origin
2161
+ };
2162
+ const startTime = (/* @__PURE__ */ new Date()).getTime();
2163
+ const url = `${customerAccountUrl}/auth/oauth/token`;
2164
+ const response = await fetch(url, {
2165
+ method: "POST",
2166
+ headers,
2167
+ body
2168
+ });
2169
+ logSubRequestEvent?.({
2170
+ displayName: "Customer Account API: access token exchange",
2171
+ url,
2172
+ startTime,
2173
+ response,
2174
+ ...debugInfo
2175
+ });
2176
+ const data = await response.json();
2177
+ if (data.error) {
2178
+ throw new BadRequest(data.error_description);
2179
+ }
2180
+ return data.access_token;
2181
+ }
2182
+ function getNonce(token) {
2183
+ return decodeJwt(token).payload.nonce;
2184
+ }
2185
+ function decodeJwt(token) {
2186
+ const [header, payload, signature] = token.split(".");
2187
+ const decodedHeader = JSON.parse(atob(header));
2188
+ const decodedPayload = JSON.parse(atob(payload));
2189
+ return {
2190
+ header: decodedHeader,
2191
+ payload: decodedPayload,
2192
+ signature
2193
+ };
2194
+ }
2195
+
2196
+ // src/csp/nonce.ts
2197
+ function generateNonce() {
2198
+ return toHexString(randomUint8Array());
2199
+ }
2200
+ function randomUint8Array() {
2201
+ try {
2202
+ return crypto.getRandomValues(new Uint8Array(16));
2203
+ } catch (e) {
2204
+ return new Uint8Array(16).map(() => Math.random() * 255 | 0);
2205
+ }
2206
+ }
2207
+ function toHexString(byteArray) {
2208
+ return Array.from(byteArray, function(byte) {
2209
+ return ("0" + (byte & 255).toString(16)).slice(-2);
2210
+ }).join("");
2211
+ }
2212
+
2213
+ // src/customer/customer.ts
2214
+ var DEFAULT_LOGIN_URL = "/account/login";
2215
+ var DEFAULT_AUTH_URL = "/account/authorize";
2216
+ var DEFAULT_REDIRECT_PATH = "/account";
2217
+ function defaultAuthStatusHandler(request) {
2218
+ if (!request.url)
2219
+ return DEFAULT_LOGIN_URL;
2220
+ const { pathname } = new URL(request.url);
2221
+ const redirectTo = DEFAULT_LOGIN_URL + `?${new URLSearchParams({ return_to: pathname }).toString()}`;
2222
+ return redirect(redirectTo);
2223
+ }
2224
+ function createCustomerAccountClient({
2225
+ session,
2226
+ customerAccountId,
2227
+ customerAccountUrl,
2228
+ customerApiVersion = DEFAULT_CUSTOMER_API_VERSION,
2229
+ request,
2230
+ waitUntil,
2231
+ authUrl = DEFAULT_AUTH_URL,
2232
+ customAuthStatusHandler,
2233
+ logErrors = true
2234
+ }) {
2235
+ if (customerApiVersion !== DEFAULT_CUSTOMER_API_VERSION) {
2236
+ console.warn(
2237
+ `[h2:warn:createCustomerAccountClient] You are using Customer Account API version ${customerApiVersion} when this version of Hydrogen was built for ${DEFAULT_CUSTOMER_API_VERSION}.`
2238
+ );
2239
+ }
2240
+ if (!customerAccountId || !customerAccountUrl) {
2241
+ console.warn(
2242
+ "[h2:warn:createCustomerAccountClient] `customerAccountId` and `customerAccountUrl` need to be provided to use Customer Account API. Mock.shop doesn't automatically supply these variables.\nUse `npx shopify hydrogen env pull` to link your store credentials."
2243
+ );
2244
+ }
2245
+ if (!request?.url) {
2246
+ throw new Error(
2247
+ "[h2:error:createCustomerAccountClient] The request object does not contain a URL."
2248
+ );
2249
+ }
2250
+ const authStatusHandler = customAuthStatusHandler ? customAuthStatusHandler : () => defaultAuthStatusHandler(request);
2251
+ const requestUrl = new URL(request.url);
2252
+ const origin = requestUrl.protocol === "http:" ? requestUrl.origin.replace("http", "https") : requestUrl.origin;
2253
+ const redirectUri = authUrl.startsWith("/") ? origin + authUrl : authUrl;
2254
+ const customerAccountApiUrl = `${customerAccountUrl}/account/customer/api/${customerApiVersion}/graphql`;
2255
+ const locks = {};
2256
+ async function fetchCustomerAPI({
2257
+ query,
2258
+ type,
2259
+ variables = {}
2260
+ }) {
2261
+ const accessToken = await getAccessToken();
2262
+ if (!accessToken) {
2263
+ throw authStatusHandler();
2264
+ }
2265
+ const stackInfo = getCallerStackLine?.();
2266
+ const startTime = (/* @__PURE__ */ new Date()).getTime();
2267
+ const response = await fetch(customerAccountApiUrl, {
2268
+ method: "POST",
2269
+ headers: {
2270
+ "Content-Type": "application/json",
2271
+ "User-Agent": USER_AGENT,
2272
+ Origin: origin,
2273
+ Authorization: accessToken
2274
+ },
2275
+ body: JSON.stringify({ query, variables })
2276
+ });
2277
+ logSubRequestEvent?.({
2278
+ url: customerAccountApiUrl,
2279
+ startTime,
2280
+ response,
2281
+ waitUntil,
2282
+ stackInfo,
2283
+ query,
2284
+ variables,
2285
+ ...getDebugHeaders(request)
2286
+ });
2287
+ const body = await response.text();
2288
+ const errorOptions = {
2289
+ url: customerAccountApiUrl,
2290
+ response,
2291
+ type,
2292
+ query,
2293
+ queryVariables: variables,
2294
+ errors: void 0,
2295
+ client: "customer"
2296
+ };
2297
+ if (!response.ok) {
2298
+ if (response.status === 401) {
2299
+ clearSession(session);
2300
+ const authFailResponse = authStatusHandler();
2301
+ if (authFailResponse instanceof Response) {
2302
+ authFailResponse.headers.set("Set-Cookie", await session.commit());
2303
+ }
2304
+ throw authFailResponse;
2305
+ }
2306
+ let errors;
2307
+ try {
2308
+ errors = parseJSON(body);
2309
+ } catch (_e) {
2310
+ errors = [{ message: body }];
2311
+ }
2312
+ throwErrorWithGqlLink({ ...errorOptions, errors });
2313
+ }
2314
+ try {
2315
+ const APIresponse = parseJSON(body);
2316
+ const { errors } = APIresponse;
2317
+ const gqlErrors = errors?.map(
2318
+ ({ message, ...rest }) => new GraphQLError(message, {
2319
+ ...rest,
2320
+ clientOperation: `customerAccount.${errorOptions.type}`,
2321
+ requestId: response.headers.get("x-request-id"),
2322
+ queryVariables: variables,
2323
+ query
2324
+ })
2325
+ );
2326
+ return { ...APIresponse, ...errors && { errors: gqlErrors } };
2327
+ } catch (e) {
2328
+ throwErrorWithGqlLink({ ...errorOptions, errors: [{ message: body }] });
2329
+ }
2330
+ }
2331
+ async function isLoggedIn() {
2332
+ const customerAccount = session.get(CUSTOMER_ACCOUNT_SESSION_KEY);
2333
+ const accessToken = customerAccount?.accessToken;
2334
+ const expiresAt = customerAccount?.expiresAt;
2335
+ if (!accessToken || !expiresAt)
2336
+ return false;
2337
+ const stackInfo = getCallerStackLine?.();
2338
+ try {
2339
+ await checkExpires({
2340
+ locks,
2341
+ expiresAt,
2342
+ session,
2343
+ customerAccountId,
2344
+ customerAccountUrl,
2345
+ origin,
2346
+ debugInfo: {
2347
+ waitUntil,
2348
+ stackInfo,
2349
+ ...getDebugHeaders(request)
2350
+ }
2351
+ });
2352
+ } catch {
2353
+ return false;
2354
+ }
2355
+ return true;
2356
+ }
2357
+ async function handleAuthStatus() {
2358
+ if (!await isLoggedIn()) {
2359
+ throw authStatusHandler();
2360
+ }
2361
+ }
2362
+ async function getAccessToken() {
2363
+ const hasAccessToken = await isLoggedIn();
2364
+ if (hasAccessToken)
2365
+ return session.get(CUSTOMER_ACCOUNT_SESSION_KEY)?.accessToken;
2366
+ }
2367
+ return {
2368
+ login: async () => {
2369
+ const loginUrl = new URL(customerAccountUrl + "/auth/oauth/authorize");
2370
+ const state = await generateState();
2371
+ const nonce = await generateNonce();
2372
+ loginUrl.searchParams.set("client_id", customerAccountId);
2373
+ loginUrl.searchParams.set("scope", "openid email");
2374
+ loginUrl.searchParams.append("response_type", "code");
2375
+ loginUrl.searchParams.append("redirect_uri", redirectUri);
2376
+ loginUrl.searchParams.set(
2377
+ "scope",
2378
+ "openid email https://api.customers.com/auth/customer.graphql"
2379
+ );
2380
+ loginUrl.searchParams.append("state", state);
2381
+ loginUrl.searchParams.append("nonce", nonce);
2382
+ const verifier = await generateCodeVerifier();
2383
+ const challenge = await generateCodeChallenge(verifier);
2384
+ session.set(CUSTOMER_ACCOUNT_SESSION_KEY, {
2385
+ ...session.get(CUSTOMER_ACCOUNT_SESSION_KEY),
2386
+ codeVerifier: verifier,
2387
+ state,
2388
+ nonce,
2389
+ redirectPath: getRedirectUrl(request.url) || getHeader(request, "Referer") || DEFAULT_REDIRECT_PATH
2390
+ });
2391
+ loginUrl.searchParams.append("code_challenge", challenge);
2392
+ loginUrl.searchParams.append("code_challenge_method", "S256");
2393
+ return redirect(loginUrl.toString(), {
2394
+ headers: {
2395
+ "Set-Cookie": await session.commit()
2396
+ }
2397
+ });
2398
+ },
2399
+ logout: async () => {
2400
+ const idToken = session.get(CUSTOMER_ACCOUNT_SESSION_KEY)?.idToken;
2401
+ clearSession(session);
2402
+ return redirect(
2403
+ `${customerAccountUrl}/auth/logout?id_token_hint=${idToken}`,
2404
+ {
2405
+ status: 302,
2406
+ headers: {
2407
+ "Set-Cookie": await session.commit()
2408
+ }
2409
+ }
2410
+ );
2411
+ },
2412
+ isLoggedIn,
2413
+ handleAuthStatus,
2414
+ getAccessToken,
2415
+ getApiUrl: () => customerAccountApiUrl,
2416
+ mutate(mutation, options) {
2417
+ mutation = minifyQuery(mutation);
2418
+ assertMutation(mutation, "customer.mutate");
2419
+ return withSyncStack(
2420
+ fetchCustomerAPI({ query: mutation, type: "mutation", ...options }),
2421
+ { logErrors }
2422
+ );
2423
+ },
2424
+ query(query, options) {
2425
+ query = minifyQuery(query);
2426
+ assertQuery(query, "customer.query");
2427
+ return withSyncStack(
2428
+ fetchCustomerAPI({ query, type: "query", ...options }),
2429
+ { logErrors }
2430
+ );
2431
+ },
2432
+ authorize: async () => {
2433
+ const code = requestUrl.searchParams.get("code");
2434
+ const state = requestUrl.searchParams.get("state");
2435
+ if (!code || !state) {
2436
+ clearSession(session);
2437
+ throw new BadRequest(
2438
+ "Unauthorized",
2439
+ "No code or state parameter found in the redirect URL.",
2440
+ {
2441
+ "Set-Cookie": await session.commit()
2442
+ }
2443
+ );
2444
+ }
2445
+ if (session.get(CUSTOMER_ACCOUNT_SESSION_KEY)?.state !== state) {
2446
+ clearSession(session);
2447
+ throw new BadRequest(
2448
+ "Unauthorized",
2449
+ "The session state does not match the state parameter. Make sure that the session is configured correctly and passed to `createCustomerAccountClient`.",
2450
+ {
2451
+ "Set-Cookie": await session.commit()
2452
+ }
2453
+ );
2454
+ }
2455
+ const clientId = customerAccountId;
2456
+ const body = new URLSearchParams();
2457
+ body.append("grant_type", "authorization_code");
2458
+ body.append("client_id", clientId);
2459
+ body.append("redirect_uri", redirectUri);
2460
+ body.append("code", code);
2461
+ const codeVerifier = session.get(
2462
+ CUSTOMER_ACCOUNT_SESSION_KEY
2463
+ )?.codeVerifier;
2464
+ if (!codeVerifier)
2465
+ throw new BadRequest(
2466
+ "Unauthorized",
2467
+ "No code verifier found in the session. Make sure that the session is configured correctly and passed to `createCustomerAccountClient`."
2468
+ );
2469
+ body.append("code_verifier", codeVerifier);
2470
+ const headers = {
2471
+ "content-type": "application/x-www-form-urlencoded",
2472
+ "User-Agent": USER_AGENT,
2473
+ Origin: origin
2474
+ };
2475
+ const stackInfo = getCallerStackLine?.();
2476
+ const startTime = (/* @__PURE__ */ new Date()).getTime();
2477
+ const url = `${customerAccountUrl}/auth/oauth/token`;
2478
+ const response = await fetch(url, {
2479
+ method: "POST",
2480
+ headers,
2481
+ body
2482
+ });
2483
+ logSubRequestEvent?.({
2484
+ url,
2485
+ displayName: "Customer Account API: authorize",
2486
+ startTime,
2487
+ response,
2488
+ waitUntil,
2489
+ stackInfo,
2490
+ ...getDebugHeaders(request)
2491
+ });
2492
+ if (!response.ok) {
2493
+ throw new Response(await response.text(), {
2494
+ status: response.status,
2495
+ headers: {
2496
+ "Content-Type": "text/html; charset=utf-8"
2497
+ }
2498
+ });
2499
+ }
2500
+ const { access_token, expires_in, id_token, refresh_token } = await response.json();
2501
+ const sessionNonce = session.get(CUSTOMER_ACCOUNT_SESSION_KEY)?.nonce;
2502
+ const responseNonce = await getNonce(id_token);
2503
+ if (sessionNonce !== responseNonce) {
2504
+ throw new BadRequest(
2505
+ "Unauthorized",
2506
+ `Returned nonce does not match: ${sessionNonce} !== ${responseNonce}`
2507
+ );
2508
+ }
2509
+ const customerAccessToken = await exchangeAccessToken(
2510
+ access_token,
2511
+ customerAccountId,
2512
+ customerAccountUrl,
2513
+ origin,
2514
+ {
2515
+ waitUntil,
2516
+ stackInfo,
2517
+ ...getDebugHeaders(request)
2518
+ }
2519
+ );
2520
+ const redirectPath = session.get(
2521
+ CUSTOMER_ACCOUNT_SESSION_KEY
2522
+ )?.redirectPath;
2523
+ session.set(CUSTOMER_ACCOUNT_SESSION_KEY, {
2524
+ accessToken: customerAccessToken,
2525
+ expiresAt: new Date(
2526
+ (/* @__PURE__ */ new Date()).getTime() + (expires_in - 120) * 1e3
2527
+ ).getTime() + "",
2528
+ refreshToken: refresh_token,
2529
+ idToken: id_token,
2530
+ redirectPath: void 0
2531
+ });
2532
+ return redirect(redirectPath || DEFAULT_REDIRECT_PATH, {
2533
+ headers: {
2534
+ "Set-Cookie": await session.commit()
2535
+ }
2536
+ });
2537
+ }
2538
+ };
2539
+ }
2540
+
2541
+ // src/changelogHandler.ts
2542
+ var DEFAULT_GITHUB_CHANGELOG_URL = "https://raw.githubusercontent.com/Shopify/hydrogen/main/docs/changelog.json";
2543
+ async function changelogHandler({
2544
+ request,
2545
+ changelogUrl
2546
+ }) {
2547
+ new URL(request.url).searchParams;
2548
+ const GITHUB_CHANGELOG_URL = changelogUrl || DEFAULT_GITHUB_CHANGELOG_URL;
2549
+ return fetch(GITHUB_CHANGELOG_URL);
2550
+ }
2551
+ var INPUT_NAME = "cartFormInput";
2552
+ function CartForm({
2553
+ children,
2554
+ action,
2555
+ inputs,
2556
+ route,
2557
+ fetcherKey
2558
+ }) {
2559
+ const fetcher = useFetcher({ key: fetcherKey });
2560
+ return /* @__PURE__ */ jsxs(fetcher.Form, { action: route || "", method: "post", children: [
2561
+ (action || inputs) && /* @__PURE__ */ jsx(
2562
+ "input",
2563
+ {
2564
+ type: "hidden",
2565
+ name: INPUT_NAME,
2566
+ value: JSON.stringify({ action, inputs })
2567
+ }
2568
+ ),
2569
+ typeof children === "function" ? children(fetcher) : children
2570
+ ] });
2571
+ }
2572
+ CartForm.INPUT_NAME = INPUT_NAME;
2573
+ CartForm.ACTIONS = {
2574
+ AttributesUpdateInput: "AttributesUpdateInput",
2575
+ BuyerIdentityUpdate: "BuyerIdentityUpdate",
2576
+ Create: "Create",
2577
+ DiscountCodesUpdate: "DiscountCodesUpdate",
2578
+ LinesAdd: "LinesAdd",
2579
+ LinesRemove: "LinesRemove",
2580
+ LinesUpdate: "LinesUpdate",
2581
+ NoteUpdate: "NoteUpdate",
2582
+ SelectedDeliveryOptionsUpdate: "SelectedDeliveryOptionsUpdate",
2583
+ MetafieldsSet: "MetafieldsSet",
2584
+ MetafieldDelete: "MetafieldDelete"
2585
+ };
2586
+ function getFormInput(formData) {
2587
+ const data = {};
2588
+ for (const pair of formData.entries()) {
2589
+ const key = pair[0];
2590
+ const values = formData.getAll(key);
2591
+ data[key] = values.length > 1 ? values : pair[1];
2592
+ }
2593
+ const { cartFormInput, ...otherData } = data;
2594
+ const { action, inputs } = cartFormInput ? JSON.parse(String(cartFormInput)) : {};
2595
+ return {
2596
+ action,
2597
+ inputs: {
2598
+ ...inputs,
2599
+ ...otherData
2600
+ }
2601
+ };
2602
+ }
2603
+ CartForm.getFormInput = getFormInput;
2604
+
2605
+ // src/cart/queries/cart-fragments.ts
2606
+ var USER_ERROR_FRAGMENT = `#graphql
2607
+ fragment CartApiError on CartUserError {
2608
+ message
2609
+ field
2610
+ code
2611
+ }
2612
+ `;
2613
+ var MINIMAL_CART_FRAGMENT = `#graphql
2614
+ fragment CartApiMutation on Cart {
2615
+ id
2616
+ totalQuantity
2617
+ }
2618
+ `;
2619
+
2620
+ // src/cart/queries/cartCreateDefault.ts
2621
+ function cartCreateDefault(options) {
2622
+ return async (input, optionalParams) => {
2623
+ const { cartId, ...restOfOptionalParams } = optionalParams || {};
2624
+ const { cartCreate, errors } = await options.storefront.mutate(CART_CREATE_MUTATION(options.cartFragment), {
2625
+ variables: {
2626
+ input,
2627
+ ...restOfOptionalParams
2628
+ }
2629
+ });
2630
+ return formatAPIResult(cartCreate, errors);
2631
+ };
2632
+ }
2633
+ var CART_CREATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
2634
+ mutation cartCreate(
2635
+ $input: CartInput!
2636
+ $country: CountryCode = ZZ
2637
+ $language: LanguageCode
2638
+ ) @inContext(country: $country, language: $language) {
2639
+ cartCreate(input: $input) {
2640
+ cart {
2641
+ ...CartApiMutation
2642
+ checkoutUrl
2643
+ }
2644
+ userErrors {
2645
+ ...CartApiError
2646
+ }
2647
+ }
2648
+ }
2649
+ ${cartFragment}
2650
+ ${USER_ERROR_FRAGMENT}
2651
+ `;
2652
+
2653
+ // src/cart/queries/cartGetDefault.ts
2654
+ function cartGetDefault({
2655
+ storefront,
2656
+ customerAccount,
2657
+ getCartId,
2658
+ cartFragment
2659
+ }) {
2660
+ return async (cartInput) => {
2661
+ const cartId = getCartId();
2662
+ if (!cartId)
2663
+ return null;
2664
+ const [isCustomerLoggedIn, { cart, errors }] = await Promise.all([
2665
+ customerAccount ? customerAccount.isLoggedIn() : false,
2666
+ storefront.query(CART_QUERY(cartFragment), {
2667
+ variables: {
2668
+ cartId,
2669
+ ...cartInput
2670
+ },
2671
+ cache: storefront.CacheNone()
2672
+ })
2673
+ ]);
2674
+ return formatAPIResult(
2675
+ addCustomerLoggedInParam(isCustomerLoggedIn, cart),
2676
+ errors
2677
+ );
2678
+ };
2679
+ }
2680
+ function addCustomerLoggedInParam(isCustomerLoggedIn, cart) {
2681
+ if (isCustomerLoggedIn && cart && cart.checkoutUrl) {
2682
+ const finalCheckoutUrl = new URL(cart.checkoutUrl);
2683
+ finalCheckoutUrl.searchParams.set("logged_in", "true");
2684
+ cart.checkoutUrl = finalCheckoutUrl.toString();
2685
+ }
2686
+ return cart;
2687
+ }
2688
+ var CART_QUERY = (cartFragment = DEFAULT_CART_FRAGMENT) => `#graphql
2689
+ query CartQuery(
2690
+ $cartId: ID!
2691
+ $numCartLines: Int = 100
2692
+ $country: CountryCode = ZZ
2693
+ $language: LanguageCode
2694
+ ) @inContext(country: $country, language: $language) {
2695
+ cart(id: $cartId) {
2696
+ ...CartApiQuery
2697
+ }
2698
+ }
2699
+
2700
+ ${cartFragment}
2701
+ `;
2702
+ var DEFAULT_CART_FRAGMENT = `#graphql
2703
+ fragment CartApiQuery on Cart {
2704
+ id
2705
+ checkoutUrl
2706
+ totalQuantity
2707
+ buyerIdentity {
2708
+ countryCode
2709
+ customer {
2710
+ id
2711
+ email
2712
+ firstName
2713
+ lastName
2714
+ displayName
2715
+ }
2716
+ email
2717
+ phone
2718
+ }
2719
+ lines(first: $numCartLines) {
2720
+ edges {
2721
+ node {
2722
+ id
2723
+ quantity
2724
+ attributes {
2725
+ key
2726
+ value
2727
+ }
2728
+ cost {
2729
+ totalAmount {
2730
+ amount
2731
+ currencyCode
2732
+ }
2733
+ amountPerQuantity {
2734
+ amount
2735
+ currencyCode
2736
+ }
2737
+ compareAtAmountPerQuantity {
2738
+ amount
2739
+ currencyCode
2740
+ }
2741
+ }
2742
+ merchandise {
2743
+ ... on ProductVariant {
2744
+ id
2745
+ availableForSale
2746
+ compareAtPrice {
2747
+ ...CartApiMoney
2748
+ }
2749
+ price {
2750
+ ...CartApiMoney
2751
+ }
2752
+ requiresShipping
2753
+ title
2754
+ image {
2755
+ ...CartApiImage
2756
+ }
2757
+ product {
2758
+ handle
2759
+ title
2760
+ id
2761
+ }
2762
+ selectedOptions {
2763
+ name
2764
+ value
2765
+ }
2766
+ }
2767
+ }
2768
+ }
2769
+ }
2770
+ }
2771
+ cost {
2772
+ subtotalAmount {
2773
+ ...CartApiMoney
2774
+ }
2775
+ totalAmount {
2776
+ ...CartApiMoney
2777
+ }
2778
+ totalDutyAmount {
2779
+ ...CartApiMoney
2780
+ }
2781
+ totalTaxAmount {
2782
+ ...CartApiMoney
2783
+ }
2784
+ }
2785
+ note
2786
+ attributes {
2787
+ key
2788
+ value
2789
+ }
2790
+ discountCodes {
2791
+ applicable
2792
+ code
2793
+ }
2794
+ }
2795
+
2796
+ fragment CartApiMoney on MoneyV2 {
2797
+ currencyCode
2798
+ amount
2799
+ }
2800
+
2801
+ fragment CartApiImage on Image {
2802
+ id
2803
+ url
2804
+ altText
2805
+ width
2806
+ height
2807
+ }
2808
+ `;
2809
+
2810
+ // src/cart/queries/cartLinesAddDefault.ts
2811
+ function cartLinesAddDefault(options) {
2812
+ return async (lines, optionalParams) => {
2813
+ const { cartLinesAdd, errors } = await options.storefront.mutate(CART_LINES_ADD_MUTATION(options.cartFragment), {
2814
+ variables: {
2815
+ cartId: options.getCartId(),
2816
+ lines,
2817
+ ...optionalParams
2818
+ }
2819
+ });
2820
+ return formatAPIResult(cartLinesAdd, errors);
2821
+ };
2822
+ }
2823
+ var CART_LINES_ADD_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
2824
+ mutation cartLinesAdd(
2825
+ $cartId: ID!
2826
+ $lines: [CartLineInput!]!
2827
+ $country: CountryCode = ZZ
2828
+ $language: LanguageCode
2829
+ ) @inContext(country: $country, language: $language) {
2830
+ cartLinesAdd(cartId: $cartId, lines: $lines) {
2831
+ cart {
2832
+ ...CartApiMutation
2833
+ }
2834
+ userErrors {
2835
+ ...CartApiError
2836
+ }
2837
+ }
2838
+ }
2839
+
2840
+ ${cartFragment}
2841
+ ${USER_ERROR_FRAGMENT}
2842
+ `;
2843
+
2844
+ // src/cart/queries/cartLinesUpdateDefault.ts
2845
+ function cartLinesUpdateDefault(options) {
2846
+ return async (lines, optionalParams) => {
2847
+ const { cartLinesUpdate, errors } = await options.storefront.mutate(CART_LINES_UPDATE_MUTATION(options.cartFragment), {
2848
+ variables: {
2849
+ cartId: options.getCartId(),
2850
+ lines,
2851
+ ...optionalParams
2852
+ }
2853
+ });
2854
+ return formatAPIResult(cartLinesUpdate, errors);
2855
+ };
2856
+ }
2857
+ var CART_LINES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
2858
+ mutation cartLinesUpdate(
2859
+ $cartId: ID!
2860
+ $lines: [CartLineUpdateInput!]!
2861
+ $language: LanguageCode
2862
+ $country: CountryCode
2863
+ ) @inContext(country: $country, language: $language) {
2864
+ cartLinesUpdate(cartId: $cartId, lines: $lines) {
2865
+ cart {
2866
+ ...CartApiMutation
2867
+ }
2868
+ userErrors {
2869
+ ...CartApiError
2870
+ }
2871
+ }
2872
+ }
2873
+
2874
+ ${cartFragment}
2875
+ ${USER_ERROR_FRAGMENT}
2876
+ `;
2877
+
2878
+ // src/cart/queries/cartLinesRemoveDefault.ts
2879
+ function cartLinesRemoveDefault(options) {
2880
+ return async (lineIds, optionalParams) => {
2881
+ const { cartLinesRemove, errors } = await options.storefront.mutate(CART_LINES_REMOVE_MUTATION(options.cartFragment), {
2882
+ variables: {
2883
+ cartId: options.getCartId(),
2884
+ lineIds,
2885
+ ...optionalParams
2886
+ }
2887
+ });
2888
+ return formatAPIResult(cartLinesRemove, errors);
2889
+ };
2890
+ }
2891
+ var CART_LINES_REMOVE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
2892
+ mutation cartLinesRemove(
2893
+ $cartId: ID!
2894
+ $lineIds: [ID!]!
2895
+ $language: LanguageCode
2896
+ $country: CountryCode
2897
+ ) @inContext(country: $country, language: $language) {
2898
+ cartLinesRemove(cartId: $cartId, lineIds: $lineIds) {
2899
+ cart {
2900
+ ...CartApiMutation
2901
+ }
2902
+ userErrors {
2903
+ ...CartApiError
2904
+ }
2905
+ }
2906
+ }
2907
+
2908
+ ${cartFragment}
2909
+ ${USER_ERROR_FRAGMENT}
2910
+ `;
2911
+
2912
+ // src/cart/queries/cartDiscountCodesUpdateDefault.ts
2913
+ function cartDiscountCodesUpdateDefault(options) {
2914
+ return async (discountCodes, optionalParams) => {
2915
+ const uniqueCodes = discountCodes.filter((value, index, array) => {
2916
+ return array.indexOf(value) === index;
2917
+ });
2918
+ const { cartDiscountCodesUpdate, errors } = await options.storefront.mutate(CART_DISCOUNT_CODE_UPDATE_MUTATION(options.cartFragment), {
2919
+ variables: {
2920
+ cartId: options.getCartId(),
2921
+ discountCodes: uniqueCodes,
2922
+ ...optionalParams
2923
+ }
2924
+ });
2925
+ return formatAPIResult(cartDiscountCodesUpdate, errors);
2926
+ };
2927
+ }
2928
+ var CART_DISCOUNT_CODE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
2929
+ mutation cartDiscountCodesUpdate(
2930
+ $cartId: ID!
2931
+ $discountCodes: [String!]
2932
+ $language: LanguageCode
2933
+ $country: CountryCode
2934
+ ) @inContext(country: $country, language: $language) {
2935
+ cartDiscountCodesUpdate(cartId: $cartId, discountCodes: $discountCodes) {
2936
+ cart {
2937
+ ...CartApiMutation
2938
+ }
2939
+ userErrors {
2940
+ ...CartApiError
2941
+ }
2942
+ }
2943
+ }
2944
+ ${cartFragment}
2945
+ ${USER_ERROR_FRAGMENT}
2946
+ `;
2947
+
2948
+ // src/cart/queries/cartBuyerIdentityUpdateDefault.ts
2949
+ function cartBuyerIdentityUpdateDefault(options) {
2950
+ return async (buyerIdentity, optionalParams) => {
2951
+ const { cartBuyerIdentityUpdate, errors } = await options.storefront.mutate(CART_BUYER_IDENTITY_UPDATE_MUTATION(options.cartFragment), {
2952
+ variables: {
2953
+ cartId: options.getCartId(),
2954
+ buyerIdentity,
2955
+ ...optionalParams
2956
+ }
2957
+ });
2958
+ return formatAPIResult(cartBuyerIdentityUpdate, errors);
2959
+ };
2960
+ }
2961
+ var CART_BUYER_IDENTITY_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
2962
+ mutation cartBuyerIdentityUpdate(
2963
+ $cartId: ID!
2964
+ $buyerIdentity: CartBuyerIdentityInput!
2965
+ $language: LanguageCode
2966
+ $country: CountryCode
2967
+ ) @inContext(country: $country, language: $language) {
2968
+ cartBuyerIdentityUpdate(cartId: $cartId, buyerIdentity: $buyerIdentity) {
2969
+ cart {
2970
+ ...CartApiMutation
2971
+ }
2972
+ userErrors {
2973
+ ...CartApiError
2974
+ }
2975
+ }
2976
+ }
2977
+ ${cartFragment}
2978
+ ${USER_ERROR_FRAGMENT}
2979
+ `;
2980
+
2981
+ // src/cart/queries/cartNoteUpdateDefault.ts
2982
+ function cartNoteUpdateDefault(options) {
2983
+ return async (note, optionalParams) => {
2984
+ const { cartNoteUpdate, errors } = await options.storefront.mutate(CART_NOTE_UPDATE_MUTATION(options.cartFragment), {
2985
+ variables: {
2986
+ cartId: options.getCartId(),
2987
+ note,
2988
+ ...optionalParams
2989
+ }
2990
+ });
2991
+ return formatAPIResult(cartNoteUpdate, errors);
2992
+ };
2993
+ }
2994
+ var CART_NOTE_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
2995
+ mutation cartNoteUpdate(
2996
+ $cartId: ID!
2997
+ $note: String
2998
+ $language: LanguageCode
2999
+ $country: CountryCode
3000
+ ) @inContext(country: $country, language: $language) {
3001
+ cartNoteUpdate(cartId: $cartId, note: $note) {
3002
+ cart {
3003
+ ...CartApiMutation
3004
+ }
3005
+ userErrors {
3006
+ ...CartApiError
3007
+ }
3008
+ }
3009
+ }
3010
+ ${cartFragment}
3011
+ ${USER_ERROR_FRAGMENT}
3012
+ `;
3013
+
3014
+ // src/cart/queries/cartSelectedDeliveryOptionsUpdateDefault.ts
3015
+ function cartSelectedDeliveryOptionsUpdateDefault(options) {
3016
+ return async (selectedDeliveryOptions, optionalParams) => {
3017
+ const { cartSelectedDeliveryOptionsUpdate, errors } = await options.storefront.mutate(CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION(options.cartFragment), {
3018
+ variables: {
3019
+ cartId: options.getCartId(),
3020
+ selectedDeliveryOptions,
3021
+ ...optionalParams
3022
+ }
3023
+ });
3024
+ return formatAPIResult(cartSelectedDeliveryOptionsUpdate, errors);
3025
+ };
3026
+ }
3027
+ var CART_SELECTED_DELIVERY_OPTIONS_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
3028
+ mutation cartSelectedDeliveryOptionsUpdate(
3029
+ $cartId: ID!
3030
+ $selectedDeliveryOptions: [CartSelectedDeliveryOptionInput!]!
3031
+ $language: LanguageCode
3032
+ $country: CountryCode
3033
+ ) @inContext(country: $country, language: $language) {
3034
+ cartSelectedDeliveryOptionsUpdate(cartId: $cartId, selectedDeliveryOptions: $selectedDeliveryOptions) {
3035
+ cart {
3036
+ ...CartApiMutation
3037
+ }
3038
+ userErrors {
3039
+ ...CartApiError
3040
+ }
3041
+ }
3042
+ }
3043
+ ${cartFragment}
3044
+ ${USER_ERROR_FRAGMENT}
3045
+ `;
3046
+
3047
+ // src/cart/queries/cartAttributesUpdateDefault.ts
3048
+ function cartAttributesUpdateDefault(options) {
3049
+ return async (attributes, optionalParams) => {
3050
+ const { cartAttributesUpdate, errors } = await options.storefront.mutate(CART_ATTRIBUTES_UPDATE_MUTATION(options.cartFragment), {
3051
+ variables: {
3052
+ cartId: optionalParams?.cartId || options.getCartId(),
3053
+ attributes
3054
+ }
3055
+ });
3056
+ return formatAPIResult(cartAttributesUpdate, errors);
3057
+ };
3058
+ }
3059
+ var CART_ATTRIBUTES_UPDATE_MUTATION = (cartFragment = MINIMAL_CART_FRAGMENT) => `#graphql
3060
+ mutation cartAttributesUpdate(
3061
+ $cartId: ID!
3062
+ $attributes: [AttributeInput!]!
3063
+ ) {
3064
+ cartAttributesUpdate(cartId: $cartId, attributes: $attributes) {
3065
+ cart {
3066
+ ...CartApiMutation
3067
+ }
3068
+ userErrors {
3069
+ ...CartApiError
3070
+ }
3071
+ }
3072
+ }
3073
+ ${cartFragment}
3074
+ ${USER_ERROR_FRAGMENT}
3075
+ `;
3076
+
3077
+ // src/cart/queries/cartMetafieldsSetDefault.ts
3078
+ function cartMetafieldsSetDefault(options) {
3079
+ return async (metafields, optionalParams) => {
3080
+ const ownerId = optionalParams?.cartId || options.getCartId();
3081
+ const metafieldsWithOwnerId = metafields.map(
3082
+ (metafield) => ({
3083
+ ...metafield,
3084
+ ownerId
3085
+ })
3086
+ );
3087
+ const { cartMetafieldsSet, errors } = await options.storefront.mutate(CART_METAFIELD_SET_MUTATION(), {
3088
+ variables: { metafields: metafieldsWithOwnerId }
3089
+ });
3090
+ return formatAPIResult(
3091
+ {
3092
+ cart: {
3093
+ id: ownerId
3094
+ },
3095
+ ...cartMetafieldsSet
3096
+ },
3097
+ errors
3098
+ );
3099
+ };
3100
+ }
3101
+ var CART_METAFIELD_SET_MUTATION = () => `#graphql
3102
+ mutation cartMetafieldsSet(
3103
+ $metafields: [CartMetafieldsSetInput!]!
3104
+ $language: LanguageCode
3105
+ $country: CountryCode
3106
+ ) @inContext(country: $country, language: $language) {
3107
+ cartMetafieldsSet(metafields: $metafields) {
3108
+ userErrors {
3109
+ code
3110
+ elementIndex
3111
+ field
3112
+ message
3113
+ }
3114
+ }
3115
+ }
3116
+ `;
3117
+
3118
+ // src/cart/queries/cartMetafieldDeleteDefault.ts
3119
+ function cartMetafieldDeleteDefault(options) {
3120
+ return async (key, optionalParams) => {
3121
+ const ownerId = optionalParams?.cartId || options.getCartId();
3122
+ const { cartMetafieldDelete, errors } = await options.storefront.mutate(CART_METAFIELD_DELETE_MUTATION(), {
3123
+ variables: {
3124
+ input: {
3125
+ ownerId,
3126
+ key
3127
+ }
3128
+ }
3129
+ });
3130
+ return formatAPIResult(
3131
+ {
3132
+ cart: {
3133
+ id: ownerId
3134
+ },
3135
+ ...cartMetafieldDelete
3136
+ },
3137
+ errors
3138
+ );
3139
+ };
3140
+ }
3141
+ var CART_METAFIELD_DELETE_MUTATION = () => `#graphql
3142
+ mutation cartMetafieldDelete(
3143
+ $input: CartMetafieldDeleteInput!
3144
+ ) {
3145
+ cartMetafieldDelete(input: $input) {
3146
+ userErrors {
3147
+ code
3148
+ field
3149
+ message
3150
+ }
3151
+ }
3152
+ }
3153
+ `;
3154
+
3155
+ // ../../node_modules/worktop/cookie/index.mjs
3156
+ var g = /* @__PURE__ */ new Set([
3157
+ "domain",
3158
+ "path",
3159
+ "max-age",
3160
+ "expires",
3161
+ "samesite",
3162
+ "secure",
3163
+ "httponly"
3164
+ ]);
3165
+ function u(a) {
3166
+ let r = {}, e, t, n = 0, m = a.split(/;\s*/g), s, i;
3167
+ for (; n < m.length; n++)
3168
+ if (t = m[n], e = t.indexOf("="), ~e) {
3169
+ if (s = t.substring(0, e++).trim(), i = t.substring(e).trim(), i[0] === '"' && (i = i.substring(1, i.length - 1)), ~i.indexOf("%"))
3170
+ try {
3171
+ i = decodeURIComponent(i);
3172
+ } catch (f) {
3173
+ }
3174
+ g.has(t = s.toLowerCase()) ? t === "expires" ? r.expires = new Date(i) : t === "max-age" ? r.maxage = +i : r[t] = i : r[s] = i;
3175
+ } else
3176
+ (s = t.trim().toLowerCase()) && (s === "httponly" || s === "secure") && (r[s] = true);
3177
+ return r;
3178
+ }
3179
+ function l(a, r, e = {}) {
3180
+ let t = a + "=" + encodeURIComponent(r);
3181
+ return e.expires && (t += "; Expires=" + new Date(e.expires).toUTCString()), e.maxage != null && e.maxage >= 0 && (t += "; Max-Age=" + (e.maxage | 0)), e.domain && (t += "; Domain=" + e.domain), e.path && (t += "; Path=" + e.path), e.samesite && (t += "; SameSite=" + e.samesite), (e.secure || e.samesite === "None") && (t += "; Secure"), e.httponly && (t += "; HttpOnly"), t;
3182
+ }
3183
+
3184
+ // src/cart/cartGetIdDefault.ts
3185
+ var cartGetIdDefault = (requestHeaders) => {
3186
+ const cookies = u(requestHeaders.get("Cookie") || "");
3187
+ return () => {
3188
+ return cookies.cart ? `gid://shopify/Cart/${cookies.cart}` : void 0;
3189
+ };
3190
+ };
3191
+
3192
+ // src/cart/cartSetIdDefault.ts
3193
+ var cartSetIdDefault = (cookieOptions) => {
3194
+ return (cartId) => {
3195
+ const headers = new Headers();
3196
+ headers.append(
3197
+ "Set-Cookie",
3198
+ l("cart", cartId.split("/").pop() || "", {
3199
+ path: "/",
3200
+ ...cookieOptions
3201
+ })
3202
+ );
3203
+ return headers;
3204
+ };
3205
+ };
3206
+
3207
+ // src/cart/createCartHandler.ts
3208
+ function createCartHandler(options) {
3209
+ const {
3210
+ getCartId,
3211
+ setCartId,
3212
+ storefront,
3213
+ customerAccount,
3214
+ cartQueryFragment,
3215
+ cartMutateFragment
3216
+ } = options;
3217
+ const mutateOptions = {
3218
+ storefront,
3219
+ getCartId,
3220
+ cartFragment: cartMutateFragment
3221
+ };
3222
+ const cartId = getCartId();
3223
+ const cartCreate = cartCreateDefault(mutateOptions);
3224
+ const methods = {
3225
+ get: cartGetDefault({
3226
+ storefront,
3227
+ customerAccount,
3228
+ getCartId,
3229
+ cartFragment: cartQueryFragment
3230
+ }),
3231
+ getCartId,
3232
+ setCartId,
3233
+ create: cartCreate,
3234
+ addLines: async (lines, optionalParams) => {
3235
+ return cartId || optionalParams?.cartId ? await cartLinesAddDefault(mutateOptions)(lines, optionalParams) : await cartCreate({ lines }, optionalParams);
3236
+ },
3237
+ updateLines: cartLinesUpdateDefault(mutateOptions),
3238
+ removeLines: cartLinesRemoveDefault(mutateOptions),
3239
+ updateDiscountCodes: async (discountCodes, optionalParams) => {
3240
+ return cartId || optionalParams?.cartId ? await cartDiscountCodesUpdateDefault(mutateOptions)(
3241
+ discountCodes,
3242
+ optionalParams
3243
+ ) : await cartCreate({ discountCodes }, optionalParams);
3244
+ },
3245
+ updateBuyerIdentity: async (buyerIdentity, optionalParams) => {
3246
+ return cartId || optionalParams?.cartId ? await cartBuyerIdentityUpdateDefault(mutateOptions)(
3247
+ buyerIdentity,
3248
+ optionalParams
3249
+ ) : await cartCreate({ buyerIdentity }, optionalParams);
3250
+ },
3251
+ updateNote: async (note, optionalParams) => {
3252
+ return cartId || optionalParams?.cartId ? await cartNoteUpdateDefault(mutateOptions)(note, optionalParams) : await cartCreate({ note }, optionalParams);
3253
+ },
3254
+ updateSelectedDeliveryOption: cartSelectedDeliveryOptionsUpdateDefault(mutateOptions),
3255
+ updateAttributes: async (attributes, optionalParams) => {
3256
+ return cartId || optionalParams?.cartId ? await cartAttributesUpdateDefault(mutateOptions)(
3257
+ attributes,
3258
+ optionalParams
3259
+ ) : await cartCreate({ attributes }, optionalParams);
3260
+ },
3261
+ setMetafields: async (metafields, optionalParams) => {
3262
+ return cartId || optionalParams?.cartId ? await cartMetafieldsSetDefault(mutateOptions)(
3263
+ metafields,
3264
+ optionalParams
3265
+ ) : await cartCreate({ metafields }, optionalParams);
3266
+ },
3267
+ deleteMetafield: cartMetafieldDeleteDefault(mutateOptions)
3268
+ };
3269
+ if ("customMethods" in options) {
3270
+ return {
3271
+ ...methods,
3272
+ ...options.customMethods ?? {}
3273
+ };
3274
+ } else {
3275
+ return methods;
3276
+ }
3277
+ }
3278
+ function VariantSelector({
3279
+ handle,
3280
+ options = [],
3281
+ variants: _variants = [],
3282
+ productPath = "products",
3283
+ children
3284
+ }) {
3285
+ const variants = _variants instanceof Array ? _variants : flattenConnection(_variants);
3286
+ const { searchParams, path, alreadyOnProductPage } = useVariantPath(
3287
+ handle,
3288
+ productPath
3289
+ );
3290
+ const optionsWithOnlyOneValue = options.filter(
3291
+ (option) => option?.values?.length === 1
3292
+ );
3293
+ return createElement(
3294
+ Fragment,
3295
+ null,
3296
+ ...useMemo(() => {
3297
+ return options.filter((option) => option?.values?.length > 1).map((option) => {
3298
+ let activeValue;
3299
+ let availableValues = [];
3300
+ for (let value of option.values) {
3301
+ const clonedSearchParams = new URLSearchParams(
3302
+ alreadyOnProductPage ? searchParams : void 0
3303
+ );
3304
+ clonedSearchParams.set(option.name, value);
3305
+ optionsWithOnlyOneValue.forEach((option2) => {
3306
+ clonedSearchParams.set(option2.name, option2.values[0]);
3307
+ });
3308
+ const variant = variants.find(
3309
+ (variant2) => variant2?.selectedOptions?.every(
3310
+ (selectedOption) => clonedSearchParams.get(selectedOption?.name) === selectedOption?.value
3311
+ )
3312
+ );
3313
+ const currentParam = searchParams.get(option.name);
3314
+ const calculatedActiveValue = currentParam ? (
3315
+ // If a URL parameter exists for the current option, check if it equals the current value
3316
+ currentParam === value
3317
+ ) : false;
3318
+ if (calculatedActiveValue) {
3319
+ activeValue = value;
3320
+ }
3321
+ const searchString = "?" + clonedSearchParams.toString();
3322
+ availableValues.push({
3323
+ value,
3324
+ isAvailable: variant ? variant.availableForSale : true,
3325
+ to: path + searchString,
3326
+ search: searchString,
3327
+ isActive: calculatedActiveValue
3328
+ });
3329
+ }
3330
+ return children({
3331
+ option: {
3332
+ name: option.name,
3333
+ value: activeValue,
3334
+ values: availableValues
3335
+ }
3336
+ });
3337
+ });
3338
+ }, [options, variants, children])
3339
+ );
3340
+ }
3341
+ var getSelectedProductOptions = (request) => {
3342
+ if (typeof request?.url === "undefined")
3343
+ throw new TypeError(`Expected a Request instance, got ${typeof request}`);
3344
+ const searchParams = new URL(request.url).searchParams;
3345
+ const selectedOptions = [];
3346
+ searchParams.forEach((value, name) => {
3347
+ selectedOptions.push({ name, value });
3348
+ });
3349
+ return selectedOptions;
3350
+ };
3351
+ function useVariantPath(handle, productPath) {
3352
+ const { pathname, search } = useLocation();
3353
+ return useMemo(() => {
3354
+ const match = /(\/[a-zA-Z]{2}-[a-zA-Z]{2}\/)/g.exec(pathname);
3355
+ const isLocalePathname = match && match.length > 0;
3356
+ productPath = productPath.startsWith("/") ? productPath.substring(1) : productPath;
3357
+ const path = isLocalePathname ? `${match[0]}${productPath}/${handle}` : `/${productPath}/${handle}`;
3358
+ const searchParams = new URLSearchParams(search);
3359
+ return {
3360
+ searchParams,
3361
+ // If the current pathname matches the product page, we need to make sure
3362
+ // that we append to the current search params. Otherwise all the search
3363
+ // params can be generated new.
3364
+ alreadyOnProductPage: path === pathname,
3365
+ path
3366
+ };
3367
+ }, [pathname, search, handle, productPath]);
3368
+ }
3369
+ var NonceContext = createContext(void 0);
3370
+ var NonceProvider = NonceContext.Provider;
3371
+ var useNonce = () => useContext(NonceContext);
3372
+ function createContentSecurityPolicy(directives = {}) {
3373
+ const nonce = generateNonce();
3374
+ const header = createCSPHeader(nonce, directives);
3375
+ const Provider = ({ children }) => {
3376
+ return createElement(NonceProvider, { value: nonce }, children);
3377
+ };
3378
+ return {
3379
+ nonce,
3380
+ header,
3381
+ NonceProvider: Provider
3382
+ };
3383
+ }
3384
+ function createCSPHeader(nonce, directives = {}) {
3385
+ const nonceString = `'nonce-${nonce}'`;
3386
+ const styleSrc = ["'self'", "'unsafe-inline'", "https://cdn.shopify.com"];
3387
+ const connectSrc = ["'self'", "https://monorail-edge.shopifysvc.com"];
3388
+ const defaultSrc = [
3389
+ "'self'",
3390
+ nonceString,
3391
+ "https://cdn.shopify.com",
3392
+ // Used for the Customer Account API
3393
+ "https://shopify.com"
3394
+ ];
3395
+ const defaultDirectives = {
3396
+ baseUri: ["'self'"],
3397
+ defaultSrc,
3398
+ frameAncestors: ["none"],
3399
+ styleSrc,
3400
+ connectSrc
3401
+ };
3402
+ {
3403
+ defaultDirectives.styleSrc = [...styleSrc, "http://localhost:*"];
3404
+ defaultDirectives.defaultSrc = [...defaultSrc, "http://localhost:*"];
3405
+ defaultDirectives.connectSrc = [
3406
+ ...connectSrc,
3407
+ "http://localhost:*",
3408
+ // For HMR:
3409
+ "ws://localhost:*",
3410
+ "ws://127.0.0.1:*"
3411
+ ];
3412
+ }
3413
+ const combinedDirectives = Object.assign({}, defaultDirectives, directives);
3414
+ for (const key in defaultDirectives) {
3415
+ if (directives[key]) {
3416
+ combinedDirectives[key] = addCspDirective(
3417
+ directives[key],
3418
+ defaultDirectives[key]
3419
+ );
3420
+ }
3421
+ }
3422
+ if (combinedDirectives.scriptSrc instanceof Array && !combinedDirectives.scriptSrc.includes(nonceString)) {
3423
+ combinedDirectives.scriptSrc.push(nonceString);
3424
+ } else if (combinedDirectives.defaultSrc instanceof Array && !combinedDirectives.defaultSrc.includes(nonceString)) {
3425
+ combinedDirectives.defaultSrc.push(nonceString);
3426
+ }
3427
+ return cspBuilder({
3428
+ directives: combinedDirectives
3429
+ });
3430
+ }
3431
+ function addCspDirective(currentValue, value) {
3432
+ const normalizedValue = typeof value === "string" ? [value] : value;
3433
+ const normalizedCurrentValue = Array.isArray(currentValue) ? currentValue : [String(currentValue)];
3434
+ const newValue = Array.isArray(normalizedValue) ? [...normalizedCurrentValue, ...normalizedValue] : normalizedValue;
3435
+ return newValue;
3436
+ }
3437
+ var Script = forwardRef(
3438
+ (props, ref) => {
3439
+ const nonce = useNonce();
3440
+ return /* @__PURE__ */ jsx("script", { suppressHydrationWarning: true, ...props, nonce, ref });
3441
+ }
3442
+ );
3443
+ function useOptimisticData(identifier) {
3444
+ const fetchers = useFetchers();
3445
+ const data = {};
3446
+ for (const { formData } of fetchers) {
3447
+ if (formData?.get("optimistic-identifier") === identifier) {
3448
+ try {
3449
+ if (formData.has("optimistic-data")) {
3450
+ const dataInForm = JSON.parse(
3451
+ String(formData.get("optimistic-data"))
3452
+ );
3453
+ Object.assign(data, dataInForm);
3454
+ }
3455
+ } catch {
3456
+ }
3457
+ }
3458
+ }
3459
+ return data;
3460
+ }
3461
+ function OptimisticInput({ id, data }) {
3462
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [
3463
+ /* @__PURE__ */ jsx("input", { type: "hidden", name: "optimistic-identifier", value: id }),
3464
+ /* @__PURE__ */ jsx(
3465
+ "input",
3466
+ {
3467
+ type: "hidden",
3468
+ name: "optimistic-data",
3469
+ value: JSON.stringify(data)
3470
+ }
3471
+ )
3472
+ ] });
3473
+ }
3474
+ function ShopPayButton(props) {
3475
+ return /* @__PURE__ */ jsx(ShopPayButton$1, { channel: "hydrogen", ...props });
3476
+ }
3477
+ //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartCreate
3478
+ //! @see https://shopify.dev/docs/api/storefront/latest/queries/cart
3479
+ //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesAdd
3480
+ //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesUpdate
3481
+ //! @see: https://shopify.dev/docs/api/storefront/latest/mutations/cartLinesRemove
3482
+ //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartDiscountCodesUpdate
3483
+ //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartBuyerIdentityUpdate
3484
+ //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartNoteUpdate
3485
+ //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartSelectedDeliveryOptionsUpdate
3486
+ //! @see https://shopify.dev/docs/api/storefront/latest/mutations/cartMetafieldsSet
3487
+ //! @see https://shopify.dev/docs/api/storefront/2024-01/mutations/cartMetafieldDelete
3488
+
3489
+ export { CacheCustom, CacheLong, CacheNone, CacheShort, CartForm, InMemoryCache, OptimisticInput, Pagination, Script, Seo, ShopPayButton, VariantSelector, cartAttributesUpdateDefault, cartBuyerIdentityUpdateDefault, cartCreateDefault, cartDiscountCodesUpdateDefault, cartGetDefault, cartGetIdDefault, cartLinesAddDefault, cartLinesRemoveDefault, cartLinesUpdateDefault, cartMetafieldDeleteDefault, cartMetafieldsSetDefault, cartNoteUpdateDefault, cartSelectedDeliveryOptionsUpdateDefault, cartSetIdDefault, changelogHandler, createCartHandler, createContentSecurityPolicy, createCustomerAccountClient, createStorefrontClient, createWithCache, formatAPIResult, generateCacheControlHeader, getPaginationVariables, getSelectedProductOptions, graphiqlLoader, storefrontRedirect, useNonce, useOptimisticData };
3490
+ //# sourceMappingURL=out.js.map
3491
+ //# sourceMappingURL=index.js.map