hydrogen-sfdgspsdmq-test1 0.0.1-security → 2024.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -0,0 +1,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