hydrogen-sfdgspsdmq-test1 0.0.1-security → 2024.1.12

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

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