@occluze/tether 0.3.5 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tether.js CHANGED
@@ -6,6 +6,16 @@
6
6
  };
7
7
 
8
8
  // src/config.js
9
+ var config_exports = {};
10
+ __export(config_exports, {
11
+ DEFAULTS: () => DEFAULTS,
12
+ cfg: () => cfg,
13
+ configure: () => configure,
14
+ isConfigured: () => isConfigured,
15
+ log: () => log,
16
+ reportError: () => reportError,
17
+ warn: () => warn
18
+ });
9
19
  var DEFAULTS = {
10
20
  // Shopify Storefront API
11
21
  domain: "",
@@ -41,6 +51,8 @@
41
51
  // defaults to `tether:cart:<domain>`
42
52
  cartDrawerOpenClass: "is-open",
43
53
  openCartOnAdd: true,
54
+ cartFocus: true,
55
+ // move focus into the open cart, keep Tab inside it, restore it on close
44
56
  checkoutTarget: "_self",
45
57
  // '_self' or '_blank'
46
58
  // Subscriptions
@@ -51,6 +63,8 @@
51
63
  // mount document on DOMContentLoaded
52
64
  observe: false,
53
65
  // auto-mount nodes added later (CMS load-more, filters)
66
+ partsUrl: null,
67
+ // folder of tether.markets/diagnose(.min).js for the <script> build (default: next to it)
54
68
  // Formatting
55
69
  formatMoney: null,
56
70
  // (money, locale) => string
@@ -98,6 +112,14 @@
98
112
  }
99
113
 
100
114
  // src/client.js
115
+ var client_exports = {};
116
+ __export(client_exports, {
117
+ TetherError: () => TetherError,
118
+ endpoint: () => endpoint,
119
+ gql: () => gql,
120
+ unwrap: () => unwrap,
121
+ withContext: () => withContext
122
+ });
101
123
  var TetherError = class extends Error {
102
124
  constructor(message, { status, errors, userErrors, code } = {}) {
103
125
  super(message);
@@ -123,7 +145,6 @@
123
145
  }
124
146
  var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
125
147
  async function gql(query, variables = {}, { context = true } = {}) {
126
- var _a;
127
148
  if (!cfg.domain || !cfg.token) {
128
149
  throw new TetherError("Tether is not configured: domain and token are required", { code: "NOT_CONFIGURED" });
129
150
  }
@@ -160,16 +181,13 @@
160
181
  } catch {
161
182
  throw new TetherError(`Invalid response (${res.status})`, { status: res.status });
162
183
  }
163
- if ((_a = json.errors) == null ? void 0 : _a.length) {
184
+ if (json.errors?.length) {
164
185
  const inventoryDenied = json.errors.some((e) => /quantityAvailable/.test(e.message || ""));
165
186
  if (inventoryDenied && /\bquantityAvailable\b/.test(body)) {
166
187
  log("no inventory permission, retrying without quantityAvailable");
167
188
  return gql(query.replace(/\bquantityAvailable\b/g, ""), variables, { context });
168
189
  }
169
- const throttled = json.errors.some((e) => {
170
- var _a2;
171
- return ((_a2 = e.extensions) == null ? void 0 : _a2.code) === "THROTTLED";
172
- });
190
+ const throttled = json.errors.some((e) => e.extensions?.code === "THROTTLED");
173
191
  if (throttled && attempt++ < cfg.retries) {
174
192
  await sleep(500 * 2 ** attempt);
175
193
  continue;
@@ -192,7 +210,24 @@
192
210
  return payload;
193
211
  }
194
212
 
213
+ // src/cart.js
214
+ var cart_exports = {};
215
+ __export(cart_exports, {
216
+ Cart: () => Cart,
217
+ focusables: () => focusables,
218
+ toLineInput: () => toLineInput
219
+ });
220
+
195
221
  // src/fragments.js
222
+ var fragments_exports = {};
223
+ __export(fragments_exports, {
224
+ ALLOCATION_FIELDS: () => ALLOCATION_FIELDS,
225
+ SELLING_PLAN_FIELDS: () => SELLING_PLAN_FIELDS,
226
+ cartFragment: () => cartFragment,
227
+ collectionFields: () => collectionFields,
228
+ metafieldIdentifiers: () => metafieldIdentifiers,
229
+ productFragment: () => productFragment
230
+ });
196
231
  var MONEY = "amount currencyCode";
197
232
  var IMAGE = "url altText width height";
198
233
  function metafieldIdentifiers() {
@@ -208,112 +243,15 @@
208
243
  const ids = metafieldIdentifiers();
209
244
  if (!ids.length) return "";
210
245
  const list = ids.map((m) => `{namespace: ${JSON.stringify(m.namespace)}, key: ${JSON.stringify(m.key)}}`).join(", ");
211
- return `metafields(identifiers: [${list}]) {
212
- namespace key type value
213
- reference { ... on MediaImage { image { ${IMAGE} } } }
214
- }`;
215
- }
216
- var SELLING_PLAN_FIELDS = `
217
- id name description recurringDeliveries
218
- options { name value }
219
- priceAdjustments {
220
- orderCount
221
- adjustmentValue {
222
- __typename
223
- ... on SellingPlanPercentagePriceAdjustment { adjustmentPercentage }
224
- ... on SellingPlanFixedAmountPriceAdjustment { adjustmentAmount { ${MONEY} } }
225
- ... on SellingPlanFixedPriceAdjustment { price { ${MONEY} } }
226
- }
227
- }`;
228
- var ALLOCATION_FIELDS = `
229
- sellingPlan { id }
230
- checkoutChargeAmount { ${MONEY} }
231
- remainingBalanceChargeAmount { ${MONEY} }
232
- priceAdjustments {
233
- price { ${MONEY} }
234
- compareAtPrice { ${MONEY} }
235
- perDeliveryPrice { ${MONEY} }
236
- unitPrice { ${MONEY} }
237
- }`;
238
- function productFragment() {
239
- return `
240
- fragment TetherProduct on Product {
241
- id handle title description descriptionHtml productType vendor tags
242
- availableForSale publishedAt onlineStoreUrl requiresSellingPlan
243
- seo { title description }
244
- featuredImage { ${IMAGE} }
245
- images(first: ${cfg.imagesFirst}) { nodes { ${IMAGE} } }
246
- options { name optionValues { name swatch { color image { previewImage { ${IMAGE} } } } } }
247
- priceRange { minVariantPrice { ${MONEY} } maxVariantPrice { ${MONEY} } }
248
- compareAtPriceRange { minVariantPrice { ${MONEY} } maxVariantPrice { ${MONEY} } }
249
- ${metafieldsField()}
250
- sellingPlanGroups(first: ${cfg.sellingPlanGroupsFirst}) {
251
- nodes {
252
- name appName
253
- options { name values }
254
- sellingPlans(first: ${cfg.sellingPlansFirst}) { nodes { ${SELLING_PLAN_FIELDS} } }
255
- }
256
- }
257
- variants(first: ${cfg.variantsFirst}) {
258
- nodes {
259
- id title availableForSale currentlyNotInStock quantityAvailable sku barcode
260
- selectedOptions { name value }
261
- image { ${IMAGE} }
262
- price { ${MONEY} }
263
- compareAtPrice { ${MONEY} }
264
- unitPrice { ${MONEY} }
265
- sellingPlanAllocations(first: ${cfg.sellingPlansFirst}) { nodes { ${ALLOCATION_FIELDS} } }
266
- }
246
+ return `metafields(identifiers:[${list}]){namespace key type value reference{... on MediaImage{image{${IMAGE}}}}}`;
267
247
  }
268
- }`;
248
+ var SELLING_PLAN_FIELDS = ` id name description recurringDeliveries options{name value}priceAdjustments{orderCount adjustmentValue{__typename ... on SellingPlanPercentagePriceAdjustment{adjustmentPercentage}... on SellingPlanFixedAmountPriceAdjustment{adjustmentAmount{${MONEY}}}... on SellingPlanFixedPriceAdjustment{price{${MONEY}}}}}`;
249
+ var ALLOCATION_FIELDS = ` sellingPlan{id}checkoutChargeAmount{${MONEY}}remainingBalanceChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}unitPrice{${MONEY}}}`;
250
+ function productFragment() {
251
+ return ` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${IMAGE}}images(first:${cfg.imagesFirst}){nodes{${IMAGE}}}options{name optionValues{name swatch{color image{previewImage{${IMAGE}}}}}}priceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}compareAtPriceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}${metafieldsField()} sellingPlanGroups(first:${cfg.sellingPlanGroupsFirst}){nodes{name appName options{name values}sellingPlans(first:${cfg.sellingPlansFirst}){nodes{${SELLING_PLAN_FIELDS}}}}}variants(first:${cfg.variantsFirst}){nodes{id title availableForSale currentlyNotInStock quantityAvailable sku barcode selectedOptions{name value}image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}unitPrice{${MONEY}}sellingPlanAllocations(first:${cfg.sellingPlansFirst}){nodes{${ALLOCATION_FIELDS}}}}}}`;
269
252
  }
270
253
  function cartFragment() {
271
- return `
272
- fragment TetherCart on Cart {
273
- id checkoutUrl totalQuantity note createdAt updatedAt
274
- attributes { key value }
275
- buyerIdentity { countryCode email }
276
- discountCodes { code applicable }
277
- cost {
278
- subtotalAmount { ${MONEY} }
279
- totalAmount { ${MONEY} }
280
- checkoutChargeAmount { ${MONEY} }
281
- }
282
- lines(first: 100) {
283
- nodes {
284
- id quantity
285
- attributes { key value }
286
- cost {
287
- totalAmount { ${MONEY} }
288
- subtotalAmount { ${MONEY} }
289
- amountPerQuantity { ${MONEY} }
290
- compareAtAmountPerQuantity { ${MONEY} }
291
- }
292
- discountAllocations { discountedAmount { ${MONEY} } }
293
- sellingPlanAllocation {
294
- sellingPlan { id name description recurringDeliveries options { name value } }
295
- checkoutChargeAmount { ${MONEY} }
296
- priceAdjustments { price { ${MONEY} } compareAtPrice { ${MONEY} } perDeliveryPrice { ${MONEY} } }
297
- }
298
- merchandise {
299
- ... on ProductVariant {
300
- id title sku availableForSale requiresShipping
301
- image { ${IMAGE} }
302
- price { ${MONEY} }
303
- compareAtPrice { ${MONEY} }
304
- selectedOptions { name value }
305
- product { id title handle vendor featuredImage { ${IMAGE} } }
306
- }
307
- }
308
- ... on ComponentizableCartLine {
309
- lineComponents {
310
- id quantity
311
- merchandise { ... on ProductVariant { id title product { title handle } } }
312
- }
313
- }
314
- }
315
- }
316
- }`;
254
+ return ` fragment TetherCart on Cart{id checkoutUrl totalQuantity note createdAt updatedAt attributes{key value}buyerIdentity{countryCode email}discountCodes{code applicable}cost{subtotalAmount{${MONEY}}totalAmount{${MONEY}}checkoutChargeAmount{${MONEY}}}lines(first:100){nodes{id quantity attributes{key value}cost{totalAmount{${MONEY}}subtotalAmount{${MONEY}}amountPerQuantity{${MONEY}}compareAtAmountPerQuantity{${MONEY}}}discountAllocations{discountedAmount{${MONEY}}}sellingPlanAllocation{sellingPlan{id name description recurringDeliveries options{name value}}checkoutChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}}}merchandise{... on ProductVariant{id title sku availableForSale requiresShipping image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}selectedOptions{name value}product{id title handle vendor featuredImage{${IMAGE}}}}}... on ComponentizableCartLine{lineComponents{id quantity merchandise{... on ProductVariant{id title product{title handle}}}}}}}}`;
317
255
  }
318
256
  function collectionFields() {
319
257
  return `
@@ -323,6 +261,15 @@ fragment TetherCart on Cart {
323
261
  }
324
262
 
325
263
  // src/loader.js
264
+ var loader_exports = {};
265
+ __export(loader_exports, {
266
+ clearProductCache: () => clearProductCache,
267
+ getProduct: () => getProduct,
268
+ getProducts: () => getProducts,
269
+ getRecommendations: () => getRecommendations,
270
+ normalizeProductKey: () => normalizeProductKey,
271
+ primeProduct: () => primeProduct
272
+ });
326
273
  var productCache = /* @__PURE__ */ new Map();
327
274
  var pending = [];
328
275
  var resolvers = /* @__PURE__ */ new Map();
@@ -330,7 +277,7 @@ fragment TetherCart on Cart {
330
277
  var generation = 0;
331
278
  var isGid = (v) => typeof v === "string" && v.startsWith("gid://");
332
279
  function normalizeProductKey(raw) {
333
- const v = String(raw != null ? raw : "").trim();
280
+ const v = String(raw ?? "").trim();
334
281
  if (!v) return null;
335
282
  if (isGid(v)) return v;
336
283
  if (/^\d+$/.test(v)) return `gid://shopify/Product/${v}`;
@@ -366,22 +313,19 @@ fragment TetherCart on Cart {
366
313
  const ids = keys.filter(isGid);
367
314
  const handles = keys.filter((k) => !isGid(k));
368
315
  const defs = [];
369
- const parts = [];
316
+ const parts2 = [];
370
317
  const vars = {};
371
318
  if (ids.length) {
372
319
  defs.push("$ids: [ID!]!");
373
320
  vars.ids = ids;
374
- parts.push("byId: nodes(ids: $ids) { ... on Product { ...TetherProduct } }");
321
+ parts2.push("byId: nodes(ids: $ids) { ... on Product { ...TetherProduct } }");
375
322
  }
376
323
  handles.forEach((h, i) => {
377
324
  defs.push(`$h${i}: String!`);
378
325
  vars[`h${i}`] = h;
379
- parts.push(`h${i}: product(handle: $h${i}) { ...TetherProduct }`);
326
+ parts2.push(`h${i}: product(handle: $h${i}) { ...TetherProduct }`);
380
327
  });
381
- const query = `query TetherProducts(${defs.join(", ")}) {
382
- ${parts.join("\n ")}
383
- }
384
- ${productFragment()}`;
328
+ const query = `query TetherProducts(${defs.join(",")}){${parts2.join(" ")}}${productFragment()}`;
385
329
  log("fetch products", keys);
386
330
  const settle = (k, fn) => {
387
331
  const r = resolvers.get(k);
@@ -428,16 +372,7 @@ ${productFragment()}`;
428
372
  } = {}) {
429
373
  const fragment = productFragment();
430
374
  if (collection) {
431
- const data2 = await gql(`query TetherCollection($handle: String!, $first: Int!, $after: String, $sortKey: ProductCollectionSortKeys, $reverse: Boolean, $filters: [ProductFilter!]) {
432
- collection(handle: $handle) {
433
- ${collectionFields()}
434
- products(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse, filters: $filters) {
435
- nodes { ...TetherProduct }
436
- pageInfo { hasNextPage endCursor }
437
- }
438
- }
439
- }
440
- ${fragment}`, {
375
+ const data2 = await gql(`query TetherCollection($handle:String!,$first:Int!,$after:String,$sortKey:ProductCollectionSortKeys,$reverse:Boolean,$filters:[ProductFilter!]){collection(handle:$handle){${collectionFields()} products(first:$first,after:$after,sortKey:$sortKey,reverse:$reverse,filters:$filters){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}}${fragment}`, {
441
376
  handle: collection,
442
377
  first,
443
378
  after,
@@ -450,21 +385,12 @@ ${fragment}`, {
450
385
  products.nodes.forEach(primeProduct);
451
386
  return { collection: rest, products: products.nodes, pageInfo: products.pageInfo };
452
387
  }
453
- const data = await gql(`query TetherSearch($first: Int!, $after: String, $query: String, $sortKey: ProductSortKeys, $reverse: Boolean) {
454
- products(first: $first, after: $after, query: $query, sortKey: $sortKey, reverse: $reverse) {
455
- nodes { ...TetherProduct }
456
- pageInfo { hasNextPage endCursor }
457
- }
458
- }
459
- ${fragment}`, { first, after, query: query || void 0, sortKey: sortKey || void 0, reverse });
388
+ const data = await gql(`query TetherSearch($first:Int!,$after:String,$query:String,$sortKey:ProductSortKeys,$reverse:Boolean){products(first:$first,after:$after,query:$query,sortKey:$sortKey,reverse:$reverse){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}${fragment}`, { first, after, query: query || void 0, sortKey: sortKey || void 0, reverse });
460
389
  data.products.nodes.forEach(primeProduct);
461
390
  return { collection: null, products: data.products.nodes, pageInfo: data.products.pageInfo };
462
391
  }
463
392
  async function getRecommendations(productId, intent = "RELATED") {
464
- const data = await gql(`query TetherRecs($id: ID!, $intent: ProductRecommendationIntent) {
465
- productRecommendations(productId: $id, intent: $intent) { ...TetherProduct }
466
- }
467
- ${productFragment()}`, { id: productId, intent });
393
+ const data = await gql(`query TetherRecs($id:ID!,$intent:ProductRecommendationIntent){productRecommendations(productId:$id,intent:$intent){...TetherProduct}}${productFragment()}`, { id: productId, intent });
468
394
  const list = data.productRecommendations || [];
469
395
  list.forEach(primeProduct);
470
396
  return list;
@@ -491,49 +417,38 @@ ${productFragment()}`, { id: productId, intent });
491
417
  sellingPlan: (v) => toGid(v, "SellingPlan")
492
418
  };
493
419
  function toGid(value, type) {
494
- const v = String(value != null ? value : "").trim();
420
+ const v = String(value ?? "").trim();
495
421
  if (!v) return null;
496
422
  if (v.startsWith("gid://")) return v;
497
423
  if (/^\d+$/.test(v)) return `gid://shopify/${type}/${v}`;
498
424
  return v;
499
425
  }
500
426
  function allPlans(product) {
501
- var _a;
502
- const groups = ((_a = product == null ? void 0 : product.sellingPlanGroups) == null ? void 0 : _a.nodes) || [];
503
- return groups.flatMap((g) => {
504
- var _a2;
505
- return (((_a2 = g.sellingPlans) == null ? void 0 : _a2.nodes) || []).map((p) => ({ ...p, group: g }));
506
- });
427
+ const groups = product?.sellingPlanGroups?.nodes || [];
428
+ return groups.flatMap((g) => (g.sellingPlans?.nodes || []).map((p) => ({ ...p, group: g })));
507
429
  }
508
430
  function hasPlans(product) {
509
431
  return allPlans(product).length > 0;
510
432
  }
511
433
  function allocationFor(variant, planId) {
512
- var _a;
513
434
  if (!variant || !planId) return null;
514
- return (((_a = variant.sellingPlanAllocations) == null ? void 0 : _a.nodes) || []).find((a) => {
515
- var _a2;
516
- return ((_a2 = a.sellingPlan) == null ? void 0 : _a2.id) === planId;
517
- }) || null;
435
+ return (variant.sellingPlanAllocations?.nodes || []).find((a) => a.sellingPlan?.id === planId) || null;
518
436
  }
519
437
  function plansForVariant(product, variant) {
520
438
  return allPlans(product).filter((p) => allocationFor(variant, p.id));
521
439
  }
522
440
  function planPrice(variant, planId) {
523
- var _a;
524
441
  const a = allocationFor(variant, planId);
525
- const adj = (_a = a == null ? void 0 : a.priceAdjustments) == null ? void 0 : _a[0];
526
- return (adj == null ? void 0 : adj.perDeliveryPrice) || (adj == null ? void 0 : adj.price) || (variant == null ? void 0 : variant.price) || null;
442
+ const adj = a?.priceAdjustments?.[0];
443
+ return adj?.perDeliveryPrice || adj?.price || variant?.price || null;
527
444
  }
528
445
  function planCheckoutCharge(variant, planId) {
529
- var _a, _b;
530
446
  const a = allocationFor(variant, planId);
531
- return (a == null ? void 0 : a.checkoutChargeAmount) || ((_b = (_a = a == null ? void 0 : a.priceAdjustments) == null ? void 0 : _a[0]) == null ? void 0 : _b.price) || (variant == null ? void 0 : variant.price) || null;
447
+ return a?.checkoutChargeAmount || a?.priceAdjustments?.[0]?.price || variant?.price || null;
532
448
  }
533
449
  function planSavings(variant, planId) {
534
- var _a, _b;
535
- const base = parseFloat((_a = variant == null ? void 0 : variant.price) == null ? void 0 : _a.amount);
536
- const sub = parseFloat((_b = planPrice(variant, planId)) == null ? void 0 : _b.amount);
450
+ const base = parseFloat(variant?.price?.amount);
451
+ const sub = parseFloat(planPrice(variant, planId)?.amount);
537
452
  if (!Number.isFinite(base) || !Number.isFinite(sub) || base <= 0 || sub >= base) {
538
453
  return { amount: null, percentage: 0 };
539
454
  }
@@ -543,8 +458,7 @@ ${productFragment()}`, { id: productId, intent });
543
458
  };
544
459
  }
545
460
  function planAdjustment(plan) {
546
- var _a, _b;
547
- const v = (_b = (_a = plan == null ? void 0 : plan.priceAdjustments) == null ? void 0 : _a[0]) == null ? void 0 : _b.adjustmentValue;
461
+ const v = plan?.priceAdjustments?.[0]?.adjustmentValue;
548
462
  if (!v) return null;
549
463
  switch (v.__typename) {
550
464
  case "SellingPlanPercentagePriceAdjustment":
@@ -566,6 +480,35 @@ ${productFragment()}`, { id: productId, intent });
566
480
  }
567
481
 
568
482
  // src/dom.js
483
+ var dom_exports = {};
484
+ __export(dom_exports, {
485
+ attr: () => attr,
486
+ busy: () => busy,
487
+ cdnUrl: () => cdnUrl,
488
+ dataKey: () => dataKey,
489
+ emit: () => emit,
490
+ formatDate: () => formatDate,
491
+ formatMoney: () => formatMoney,
492
+ get: () => get,
493
+ has: () => has,
494
+ hasTemplate: () => hasTemplate,
495
+ notifyLayoutChanged: () => notifyLayoutChanged,
496
+ parseJSONAttr: () => parseJSONAttr,
497
+ productUrl: () => productUrl,
498
+ qsa: () => qsa,
499
+ readText: () => readText,
500
+ repeat: () => repeat,
501
+ repeatKeyed: () => repeatKeyed,
502
+ scoped: () => scoped,
503
+ sel: () => sel,
504
+ setImage: () => setImage,
505
+ setSelected: () => setSelected,
506
+ setText: () => setText,
507
+ setTextTracked: () => setTextTracked,
508
+ show: () => show,
509
+ toAttributes: () => toAttributes,
510
+ watchImages: () => watchImages
511
+ });
569
512
  var attr = (name) => `${cfg.prefix}-${name}`;
570
513
  var sel = (name, value) => value === void 0 ? `[${attr(name)}]` : `[${attr(name)}="${value}"]`;
571
514
  var get = (el, name) => el.getAttribute(attr(name));
@@ -666,19 +609,17 @@ ${productFragment()}`, { id: productId, intent });
666
609
  }
667
610
  }
668
611
  function setSelected(el, selected) {
669
- var _a;
670
612
  el.classList.toggle("is-selected", selected);
671
613
  el.setAttribute("aria-pressed", selected ? "true" : "false");
672
614
  if (el.tagName === "INPUT" && (el.type === "radio" || el.type === "checkbox")) el.checked = selected;
673
- const input = (_a = el.querySelector) == null ? void 0 : _a.call(el, 'input[type="radio"],input[type="checkbox"]');
615
+ const input = el.querySelector?.('input[type="radio"],input[type="checkbox"]');
674
616
  if (input) input.checked = selected;
675
617
  }
676
618
  var templates = /* @__PURE__ */ new WeakMap();
677
619
  var hasTemplate = (container) => Boolean(templates.get(container));
678
620
  function template(container) {
679
- var _a, _b;
680
621
  if (!templates.has(container)) {
681
- const tpl = (_b = (_a = container.firstElementChild) == null ? void 0 : _a.cloneNode(true)) != null ? _b : null;
622
+ const tpl = container.firstElementChild?.cloneNode(true) ?? null;
682
623
  templates.set(container, tpl);
683
624
  container.innerHTML = "";
684
625
  }
@@ -760,9 +701,8 @@ ${productFragment()}`, { id: productId, intent });
760
701
  function notifyLayoutChanged(reason) {
761
702
  clearTimeout(layoutTimer);
762
703
  layoutTimer = setTimeout(() => {
763
- var _a;
764
704
  if (typeof cfg.onLayoutChange === "function") cfg.onLayoutChange(reason);
765
- else if ((_a = window.ScrollTrigger) == null ? void 0 : _a.refresh) window.ScrollTrigger.refresh();
705
+ else if (window.ScrollTrigger?.refresh) window.ScrollTrigger.refresh();
766
706
  emit("layout-changed", { reason });
767
707
  }, 120);
768
708
  }
@@ -795,20 +735,16 @@ ${productFragment()}`, { id: productId, intent });
795
735
  }
796
736
  function toAttributes(obj) {
797
737
  if (!obj) return [];
798
- if (Array.isArray(obj)) return obj.map((a) => {
799
- var _a;
800
- return { key: String(a.key), value: String((_a = a.value) != null ? _a : "") };
801
- });
802
- return Object.entries(obj).map(([key, value]) => ({ key, value: String(value != null ? value : "") }));
738
+ if (Array.isArray(obj)) return obj.map((a) => ({ key: String(a.key), value: String(a.value ?? "") }));
739
+ return Object.entries(obj).map(([key, value]) => ({ key, value: String(value ?? "") }));
803
740
  }
804
741
 
805
742
  // src/cart.js
806
743
  function toLineInput(input, quantity = 1, opts = {}) {
807
- var _a;
808
744
  const src = typeof input === "object" && input !== null ? input : { variantId: input, quantity, ...opts };
809
745
  const merchandiseId = GID.variant(src.merchandiseId || src.variantId || src.id);
810
746
  if (!merchandiseId) throw new TetherError("add(): variantId is required");
811
- const line = { merchandiseId, quantity: Math.max(1, parseInt((_a = src.quantity) != null ? _a : 1, 10) || 1) };
747
+ const line = { merchandiseId, quantity: Math.max(1, parseInt(src.quantity ?? 1, 10) || 1) };
812
748
  const plan = src.sellingPlanId || src.sellingPlan;
813
749
  if (plan) line.sellingPlanId = GID.sellingPlan(plan);
814
750
  const attrs = toAttributes(src.attributes);
@@ -816,12 +752,21 @@ ${productFragment()}`, { id: productId, intent });
816
752
  return line;
817
753
  }
818
754
  function mutation(name, args, inner) {
819
- return `mutation Tether_${name}(${args}) {
820
- ${name}(${inner}) { cart { ...TetherCart } userErrors { field message } }
821
- }
822
- ${cartFragment()}`;
755
+ return `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}}}${cartFragment()}`;
756
+ }
757
+ var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
758
+ var returnFocus = null;
759
+ var focusables = (d) => qsa(d, FOCUSABLE).filter((el) => el.offsetParent !== null || el === document.activeElement);
760
+ function ariaState(open) {
761
+ qsa(document, sel("cart-open")).forEach((b) => b.setAttribute("aria-expanded", String(open)));
762
+ qsa(document, sel("cart-drawer")).forEach((d) => {
763
+ if (!d.hasAttribute("role")) d.setAttribute("role", "dialog");
764
+ if (!d.hasAttribute("aria-label") && !d.hasAttribute("aria-labelledby")) d.setAttribute("aria-label", get(d, "cart-label") || "Cart");
765
+ d.setAttribute("aria-modal", String(open));
766
+ if (!d.classList.contains(cfg.cartDrawerOpenClass)) d.setAttribute("aria-hidden", "true");
767
+ });
823
768
  }
824
- var isMissingCart = (err) => /does not exist|not found|invalid id/i.test((err == null ? void 0 : err.message) || "");
769
+ var isMissingCart = (err) => /does not exist|not found|invalid id/i.test(err?.message || "");
825
770
  var Cart = {
826
771
  state: null,
827
772
  _chain: Promise.resolve(),
@@ -840,12 +785,10 @@ ${cartFragment()}`;
840
785
  }
841
786
  },
842
787
  get lines() {
843
- var _a, _b;
844
- return ((_b = (_a = this.state) == null ? void 0 : _a.lines) == null ? void 0 : _b.nodes) || [];
788
+ return this.state?.lines?.nodes || [];
845
789
  },
846
790
  get count() {
847
- var _a;
848
- return ((_a = this.state) == null ? void 0 : _a.totalQuantity) || 0;
791
+ return this.state?.totalQuantity || 0;
849
792
  },
850
793
  lastError: null,
851
794
  _queue(fn) {
@@ -867,7 +810,7 @@ ${cartFragment()}`;
867
810
  },
868
811
  _commit(cart, reason) {
869
812
  this.state = cart;
870
- if (cart == null ? void 0 : cart.id) this._setId(cart.id);
813
+ if (cart?.id) this._setId(cart.id);
871
814
  this.render();
872
815
  emit("cart-updated", { cart, reason });
873
816
  return cart;
@@ -880,8 +823,7 @@ ${cartFragment()}`;
880
823
  return null;
881
824
  }
882
825
  try {
883
- const data = await gql(`query TetherCart($id: ID!) { cart(id: $id) { ...TetherCart } }
884
- ${cartFragment()}`, { id });
826
+ const data = await gql(`query TetherCart($id:ID!){cart(id:$id){...TetherCart}}${cartFragment()}`, { id });
885
827
  if (!data.cart) {
886
828
  this._setId(null);
887
829
  this.state = null;
@@ -966,14 +908,13 @@ ${cartFragment()}`, { id });
966
908
  },
967
909
  setNote(note) {
968
910
  return this._queue(async () => this._commit(
969
- await this._mutate("cartNoteUpdate", "$note: String!", "note: $note", { note: note != null ? note : "" }, { note: note != null ? note : "" }),
911
+ await this._mutate("cartNoteUpdate", "$note: String!", "note: $note", { note: note ?? "" }, { note: note ?? "" }),
970
912
  "note"
971
913
  ));
972
914
  },
973
915
  /** Cart-level attributes, merged with existing ones. Value '' removes a key. */
974
916
  setAttributes(obj) {
975
- var _a;
976
- const merged = new Map((((_a = this.state) == null ? void 0 : _a.attributes) || []).map((a) => [a.key, a.value]));
917
+ const merged = new Map((this.state?.attributes || []).map((a) => [a.key, a.value]));
977
918
  toAttributes(obj).forEach((a) => merged.set(a.key, a.value));
978
919
  const attributes = [...merged].filter(([, v]) => v !== "").map(([key, value]) => ({ key, value }));
979
920
  return this._queue(async () => this._commit(
@@ -990,13 +931,11 @@ ${cartFragment()}`, { id });
990
931
  ));
991
932
  },
992
933
  applyDiscount(code) {
993
- var _a;
994
- const current = (((_a = this.state) == null ? void 0 : _a.discountCodes) || []).map((d) => d.code);
934
+ const current = (this.state?.discountCodes || []).map((d) => d.code);
995
935
  return this.setDiscountCodes([.../* @__PURE__ */ new Set([...current, code])]);
996
936
  },
997
937
  removeDiscount(code) {
998
- var _a;
999
- return this.setDiscountCodes((((_a = this.state) == null ? void 0 : _a.discountCodes) || []).map((d) => d.code).filter((c) => c !== code));
938
+ return this.setDiscountCodes((this.state?.discountCodes || []).map((d) => d.code).filter((c) => c !== code));
1000
939
  },
1001
940
  /** { countryCode, email, phone } — changes currency/prices for that market. */
1002
941
  setBuyerIdentity(buyerIdentity, { create = true } = {}) {
@@ -1015,7 +954,6 @@ ${cartFragment()}`, { id });
1015
954
  * URL once we have it.
1016
955
  */
1017
956
  async checkout(target = cfg.checkoutTarget) {
1018
- var _a;
1019
957
  const id = this.id();
1020
958
  if (!id) return null;
1021
959
  const tab = target === "_blank" ? window.open("", "_blank") : null;
@@ -1027,7 +965,7 @@ ${cartFragment()}`, { id });
1027
965
  }
1028
966
  try {
1029
967
  const data = await gql("query TetherCheckout($id: ID!) { cart(id: $id) { checkoutUrl } }", { id });
1030
- const url = (_a = data.cart) == null ? void 0 : _a.checkoutUrl;
968
+ const url = data.cart?.checkoutUrl;
1031
969
  if (!url) throw new TetherError("No checkoutUrl (cart empty or expired)");
1032
970
  emit("checkout", { url, cart: this.state });
1033
971
  if (tab && !tab.closed) tab.location.href = url;
@@ -1053,16 +991,29 @@ ${cartFragment()}`, { id });
1053
991
  d.classList.add(cfg.cartDrawerOpenClass);
1054
992
  d.setAttribute("aria-hidden", "false");
1055
993
  });
994
+ ariaState(true);
995
+ if (cfg.cartFocus !== false && ds[0]) {
996
+ returnFocus = document.activeElement;
997
+ setTimeout(() => {
998
+ const target = focusables(ds[0])[0] || ds[0];
999
+ if (target === ds[0] && !ds[0].hasAttribute("tabindex")) ds[0].tabIndex = -1;
1000
+ target.focus?.({ preventScroll: true });
1001
+ }, 60);
1002
+ }
1056
1003
  if (typeof cfg.onCartOpen === "function") cfg.onCartOpen(ds);
1057
1004
  emit("cart-toggled", { open: true });
1058
1005
  },
1059
1006
  close() {
1060
1007
  if (!this.isOpen()) return;
1061
1008
  const ds = this.drawers();
1009
+ const hadFocus = ds.some((d) => d.contains(document.activeElement));
1062
1010
  ds.forEach((d) => {
1063
1011
  d.classList.remove(cfg.cartDrawerOpenClass);
1064
1012
  d.setAttribute("aria-hidden", "true");
1065
1013
  });
1014
+ ariaState(false);
1015
+ if (hadFocus && returnFocus?.isConnected) returnFocus.focus({ preventScroll: true });
1016
+ returnFocus = null;
1066
1017
  if (typeof cfg.onCartClose === "function") cfg.onCartClose(ds);
1067
1018
  emit("cart-toggled", { open: false });
1068
1019
  },
@@ -1074,10 +1025,8 @@ ${cartFragment()}`, { id });
1074
1025
  render() {
1075
1026
  const cart = this.state;
1076
1027
  const count = cart ? cart.totalQuantity : 0;
1077
- const money = (k) => {
1078
- var _a;
1079
- return cart ? formatMoney((_a = cart.cost) == null ? void 0 : _a[k]) : "";
1080
- };
1028
+ const money = (k) => cart ? formatMoney(cart.cost?.[k]) : "";
1029
+ ariaState(this.isOpen());
1081
1030
  qsa(document, sel("cart-count")).forEach((el) => setTextTracked(el, count));
1082
1031
  qsa(document, sel("cart-subtotal")).forEach((el) => setTextTracked(el, money("subtotalAmount")));
1083
1032
  qsa(document, sel("cart-total")).forEach((el) => setTextTracked(el, money("totalAmount")));
@@ -1088,7 +1037,7 @@ ${cartFragment()}`, { id });
1088
1037
  "cart-filled": count > 0,
1089
1038
  "cart-has-subscription": hasSub,
1090
1039
  "cart-no-subscription": !hasSub,
1091
- "cart-has-discount": ((cart == null ? void 0 : cart.discountCodes) || []).some((d) => d.applicable),
1040
+ "cart-has-discount": (cart?.discountCodes || []).some((d) => d.applicable),
1092
1041
  "cart-error": Boolean(this.lastError)
1093
1042
  };
1094
1043
  qsa(document, sel("show")).forEach((el) => {
@@ -1096,10 +1045,10 @@ ${cartFragment()}`, { id });
1096
1045
  if (k in conds && !el.closest(sel("product"))) show(el, conds[k]);
1097
1046
  });
1098
1047
  qsa(document, `textarea${sel("cart-note")},input${sel("cart-note")}`).forEach((el) => {
1099
- if (document.activeElement !== el) el.value = (cart == null ? void 0 : cart.note) || "";
1048
+ if (document.activeElement !== el) el.value = cart?.note || "";
1100
1049
  });
1101
1050
  qsa(document, sel("cart-discounts")).forEach((container) => {
1102
- repeat(container, (cart == null ? void 0 : cart.discountCodes) || [], (node, d) => {
1051
+ repeat(container, cart?.discountCodes || [], (node, d) => {
1103
1052
  node.setAttribute(attr("discount-code"), d.code);
1104
1053
  const t = node.querySelector(sel("discount", "code")) || (node.firstElementChild ? null : node);
1105
1054
  if (t) setText(t, d.code);
@@ -1116,60 +1065,54 @@ ${cartFragment()}`, { id });
1116
1065
  }
1117
1066
  };
1118
1067
  function lineValue(line, key) {
1119
- var _a, _b, _c, _d, _e, _f, _g, _h, _i;
1120
1068
  const m = line.merchandise || {};
1121
- const plan = (_a = line.sellingPlanAllocation) == null ? void 0 : _a.sellingPlan;
1069
+ const plan = line.sellingPlanAllocation?.sellingPlan;
1122
1070
  switch (key) {
1123
1071
  case "title":
1124
- return (_b = m.product) == null ? void 0 : _b.title;
1072
+ return m.product?.title;
1125
1073
  case "product-title":
1126
- return (_c = m.product) == null ? void 0 : _c.title;
1074
+ return m.product?.title;
1127
1075
  case "variant-title":
1128
1076
  return m.title === "Default Title" ? "" : m.title;
1129
1077
  case "options":
1130
1078
  return (m.selectedOptions || []).filter((o) => o.value !== "Default Title").map((o) => `${o.name}: ${o.value}`).join(", ");
1131
1079
  case "vendor":
1132
- return (_d = m.product) == null ? void 0 : _d.vendor;
1080
+ return m.product?.vendor;
1133
1081
  case "sku":
1134
1082
  return m.sku;
1135
1083
  case "price":
1136
- return formatMoney(((_e = line.cost) == null ? void 0 : _e.amountPerQuantity) || m.price);
1084
+ return formatMoney(line.cost?.amountPerQuantity || m.price);
1137
1085
  case "compare-at-price":
1138
- return formatMoney(((_f = line.cost) == null ? void 0 : _f.compareAtAmountPerQuantity) || m.compareAtPrice);
1086
+ return formatMoney(line.cost?.compareAtAmountPerQuantity || m.compareAtPrice);
1139
1087
  case "line-total":
1140
- return formatMoney((_g = line.cost) == null ? void 0 : _g.totalAmount);
1088
+ return formatMoney(line.cost?.totalAmount);
1141
1089
  case "line-subtotal":
1142
- return formatMoney((_h = line.cost) == null ? void 0 : _h.subtotalAmount);
1090
+ return formatMoney(line.cost?.subtotalAmount);
1143
1091
  case "quantity":
1144
1092
  return line.quantity;
1145
1093
  case "plan":
1146
- return (plan == null ? void 0 : plan.name) || "";
1094
+ return plan?.name || "";
1147
1095
  case "plan-description":
1148
- return (plan == null ? void 0 : plan.description) || "";
1096
+ return plan?.description || "";
1149
1097
  case "components":
1150
- return (line.lineComponents || []).map((c) => {
1151
- var _a2, _b2;
1152
- return `${c.quantity} \xD7 ${((_b2 = (_a2 = c.merchandise) == null ? void 0 : _a2.product) == null ? void 0 : _b2.title) || ""}`;
1153
- }).join(", ");
1098
+ return (line.lineComponents || []).map((c) => `${c.quantity} \xD7 ${c.merchandise?.product?.title || ""}`).join(", ");
1154
1099
  default:
1155
1100
  if (key.startsWith("attribute:")) {
1156
1101
  const k = key.slice("attribute:".length);
1157
- return ((_i = (line.attributes || []).find((a) => a.key === k)) == null ? void 0 : _i.value) || "";
1102
+ return (line.attributes || []).find((a) => a.key === k)?.value || "";
1158
1103
  }
1159
1104
  return void 0;
1160
1105
  }
1161
1106
  }
1162
1107
  function renderLine(node, line) {
1163
- var _a, _b, _c, _d;
1164
1108
  const m = line.merchandise || {};
1165
1109
  const isSub = Boolean(line.sellingPlanAllocation);
1166
1110
  node.setAttribute(attr("line-id"), line.id);
1167
1111
  node.classList.toggle("is-subscription", isSub);
1168
1112
  qsa(node, sel("line")).forEach((el) => {
1169
- var _a2, _b2, _c2, _d2, _e, _f;
1170
1113
  const key = get(el, "line");
1171
- if (key === "image") return setImage(el, ((_a2 = m.image) == null ? void 0 : _a2.url) || ((_c2 = (_b2 = m.product) == null ? void 0 : _b2.featuredImage) == null ? void 0 : _c2.url), (_d2 = m.product) == null ? void 0 : _d2.title);
1172
- if (key === "url") return el.setAttribute("href", productUrl((_e = m.product) == null ? void 0 : _e.handle, (_f = m.product) == null ? void 0 : _f.id));
1114
+ if (key === "image") return setImage(el, m.image?.url || m.product?.featuredImage?.url, m.product?.title);
1115
+ if (key === "url") return el.setAttribute("href", productUrl(m.product?.handle, m.product?.id));
1173
1116
  const v = lineValue(line, key);
1174
1117
  if (v !== void 0) setTextTracked(el, v);
1175
1118
  return void 0;
@@ -1177,7 +1120,7 @@ ${cartFragment()}`, { id });
1177
1120
  const lineConds = {
1178
1121
  subscription: isSub,
1179
1122
  "one-time": !isSub,
1180
- "on-sale": parseFloat((_b = (_a = line.cost) == null ? void 0 : _a.compareAtAmountPerQuantity) == null ? void 0 : _b.amount) > parseFloat((_d = (_c = line.cost) == null ? void 0 : _c.amountPerQuantity) == null ? void 0 : _d.amount),
1123
+ "on-sale": parseFloat(line.cost?.compareAtAmountPerQuantity?.amount) > parseFloat(line.cost?.amountPerQuantity?.amount),
1181
1124
  "has-components": (line.lineComponents || []).length > 0
1182
1125
  };
1183
1126
  qsa(node, sel("line-show")).forEach((el) => {
@@ -1185,11 +1128,9 @@ ${cartFragment()}`, { id });
1185
1128
  if (k in lineConds) show(el, lineConds[k]);
1186
1129
  });
1187
1130
  qsa(node, `select${sel("line-plan")}`).forEach((select) => {
1188
- var _a2;
1189
- const handle = (_a2 = m.product) == null ? void 0 : _a2.handle;
1131
+ const handle = m.product?.handle;
1190
1132
  if (!handle) return;
1191
1133
  getProduct(handle).then((product) => {
1192
- var _a3, _b2;
1193
1134
  const plans = allPlans(product);
1194
1135
  const allowOneTime = !product.requiresSellingPlan && get(select, "line-plan") !== "subscription-only";
1195
1136
  const want = [
@@ -1207,21 +1148,37 @@ ${cartFragment()}`, { id });
1207
1148
  });
1208
1149
  select.dataset.tetherSig = sig;
1209
1150
  }
1210
- select.value = ((_b2 = (_a3 = line.sellingPlanAllocation) == null ? void 0 : _a3.sellingPlan) == null ? void 0 : _b2.id) || "";
1151
+ select.value = line.sellingPlanAllocation?.sellingPlan?.id || "";
1211
1152
  show(select, plans.length > 0);
1212
1153
  }).catch((err) => reportError(err, "line plan options"));
1213
1154
  });
1214
1155
  }
1215
1156
 
1157
+ // src/product.js
1158
+ var product_exports = {};
1159
+ __export(product_exports, {
1160
+ ProductScope: () => ProductScope,
1161
+ scopes: () => scopes
1162
+ });
1163
+
1216
1164
  // src/status.js
1165
+ var status_exports = {};
1166
+ __export(status_exports, {
1167
+ STATUS_KEYS: () => STATUS_KEYS,
1168
+ boundary: () => boundary,
1169
+ isNotFound: () => isNotFound,
1170
+ ownedBy: () => ownedBy,
1171
+ resolveProductKey: () => resolveProductKey,
1172
+ setScopeStatus: () => setScopeStatus
1173
+ });
1217
1174
  function resolveProductKey(el, location = window.location) {
1218
1175
  const raw = (get(el, "product") || "").trim();
1219
1176
  if (!raw.toLowerCase().startsWith("url:")) return { key: raw || null, fromUrl: false, raw };
1220
1177
  const what = raw.slice(4).trim();
1221
1178
  let key = null;
1222
1179
  if (what.toLowerCase() === "path") {
1223
- const parts = location.pathname.split("/").filter(Boolean);
1224
- key = parts.length ? safeDecode(parts[parts.length - 1]) : null;
1180
+ const parts2 = location.pathname.split("/").filter(Boolean);
1181
+ key = parts2.length ? safeDecode(parts2[parts2.length - 1]) : null;
1225
1182
  } else {
1226
1183
  key = new URLSearchParams(location.search).get(what || "handle");
1227
1184
  }
@@ -1236,16 +1193,13 @@ ${cartFragment()}`, { id });
1236
1193
  }
1237
1194
  }
1238
1195
  var STATUS_KEYS = ["loading", "ready", "error", "not-found"];
1239
- var isNotFound = (err) => (err == null ? void 0 : err.code) === "NOT_FOUND";
1196
+ var isNotFound = (err) => err?.code === "NOT_FOUND";
1240
1197
  var boundary = () => ["product", "collection", "products", "recommendations"].map((n) => sel(n)).join(",");
1241
- var ownedBy = (el, root) => {
1242
- var _a;
1243
- return el === root || ((_a = el.parentElement) == null ? void 0 : _a.closest(boundary())) === root;
1244
- };
1198
+ var ownedBy = (el, root) => el === root || el.parentElement?.closest(boundary()) === root;
1245
1199
  function setScopeStatus(root, status, err) {
1246
1200
  const notFound = status === "not-found";
1247
1201
  root.setAttribute(attr("state"), notFound ? "error" : status);
1248
- if (status === "error" || notFound) root.setAttribute(attr("error"), notFound ? "not-found" : (err == null ? void 0 : err.code) || "error");
1202
+ if (status === "error" || notFound) root.setAttribute(attr("error"), notFound ? "not-found" : err?.code || "error");
1249
1203
  else root.removeAttribute(attr("error"));
1250
1204
  scoped(root, sel("show"), boundary()).forEach((el) => {
1251
1205
  const k = get(el, "show");
@@ -1259,6 +1213,7 @@ ${cartFragment()}`, { id });
1259
1213
  if (status === "error" || notFound) {
1260
1214
  scoped(root, sel("add-to-cart"), boundary()).forEach((btn) => {
1261
1215
  btn.classList.add("is-disabled");
1216
+ btn.setAttribute("aria-disabled", "true");
1262
1217
  if ("disabled" in btn) btn.disabled = true;
1263
1218
  });
1264
1219
  }
@@ -1276,12 +1231,11 @@ ${cartFragment()}`, { id });
1276
1231
  }
1277
1232
  var ProductScope = class {
1278
1233
  constructor(root, product) {
1279
- var _a;
1280
1234
  this.root = root;
1281
1235
  this.product = product;
1282
1236
  this.selected = {};
1283
1237
  this.variant = null;
1284
- this.quantity = Math.max(0, parseInt((_a = get(root, "quantity")) != null ? _a : "1", 10) || (has(root, "quantity") ? 0 : 1));
1238
+ this.quantity = Math.max(0, parseInt(get(root, "quantity") ?? "1", 10) || (has(root, "quantity") ? 0 : 1));
1285
1239
  this.plans = allPlans(product);
1286
1240
  this.purchase = "one-time";
1287
1241
  this.planId = null;
@@ -1299,11 +1253,10 @@ ${cartFragment()}`, { id });
1299
1253
  }
1300
1254
  /** New data for the same product (e.g. after a country switch). Keeps the selection. */
1301
1255
  update(product) {
1302
- var _a, _b;
1303
- const variantId = (_a = this.variant) == null ? void 0 : _a.id;
1256
+ const variantId = this.variant?.id;
1304
1257
  this.product = product;
1305
1258
  this.plans = allPlans(product);
1306
- if (this.planId && !this.plans.some((p) => p.id === this.planId)) this.planId = ((_b = this.plans[0]) == null ? void 0 : _b.id) || null;
1259
+ if (this.planId && !this.plans.some((p) => p.id === this.planId)) this.planId = this.plans[0]?.id || null;
1307
1260
  if (!this.plans.length && this.purchase === "subscription") this.purchase = "one-time";
1308
1261
  const v = product.variants.nodes.find((x) => x.id === variantId);
1309
1262
  if (v) {
@@ -1425,8 +1378,7 @@ ${cartFragment()}`, { id });
1425
1378
  this.renderVariant();
1426
1379
  }
1427
1380
  async addToCart() {
1428
- var _a;
1429
- if (!((_a = this.variant) == null ? void 0 : _a.availableForSale)) return null;
1381
+ if (!this.variant?.availableForSale) return null;
1430
1382
  const line = this.getLine();
1431
1383
  if (!line || line.quantity < 1) return null;
1432
1384
  const cart = await Cart.add(line);
@@ -1438,7 +1390,7 @@ ${cartFragment()}`, { id });
1438
1390
  const own = (el) => el && el.closest(productSel()) === this.root;
1439
1391
  this.root.addEventListener("click", (e) => {
1440
1392
  const t = e.target;
1441
- if (!(t == null ? void 0 : t.closest)) return;
1393
+ if (!t?.closest) return;
1442
1394
  const opt = t.closest(sel("option-value"));
1443
1395
  if (own(opt) && opt.closest(sel("option-list"))) {
1444
1396
  e.preventDefault();
@@ -1510,22 +1462,19 @@ ${cartFragment()}`, { id });
1510
1462
  renderProduct() {
1511
1463
  const p = this.product;
1512
1464
  this.$(sel("bind")).forEach((el) => {
1513
- var _a, _b, _c;
1514
- const key = get(el, "bind");
1465
+ const key = get(el, "bind") || "";
1466
+ const txt = {
1467
+ title: p.title,
1468
+ "description-text": p.description,
1469
+ vendor: p.vendor,
1470
+ type: p.productType,
1471
+ handle: p.handle
1472
+ }[key];
1473
+ if (txt !== void 0) return setText(el, txt);
1515
1474
  switch (key) {
1516
- case "title":
1517
- return setText(el, p.title);
1518
1475
  case "description":
1519
1476
  el.innerHTML = p.descriptionHtml || "";
1520
1477
  return void 0;
1521
- case "description-text":
1522
- return setText(el, p.description);
1523
- case "vendor":
1524
- return setText(el, p.vendor);
1525
- case "type":
1526
- return setText(el, p.productType);
1527
- case "handle":
1528
- return setText(el, p.handle);
1529
1478
  case "published-at":
1530
1479
  return setText(el, formatDate(p.publishedAt));
1531
1480
  case "min-price":
@@ -1533,7 +1482,7 @@ ${cartFragment()}`, { id });
1533
1482
  case "max-price":
1534
1483
  return setText(el, formatMoney(p.priceRange.maxVariantPrice));
1535
1484
  case "featured-image":
1536
- return setImage(el, (_a = p.featuredImage) == null ? void 0 : _a.url, ((_b = p.featuredImage) == null ? void 0 : _b.altText) || p.title);
1485
+ return setImage(el, p.featuredImage?.url, p.featuredImage?.altText || p.title);
1537
1486
  case "url":
1538
1487
  case "link":
1539
1488
  el.setAttribute("href", productUrl(p.handle, p.id));
@@ -1549,19 +1498,15 @@ ${cartFragment()}`, { id });
1549
1498
  return repeat(el, p.tags.filter((t) => !exclude.includes(t)), (node, tag) => setText(node, tag));
1550
1499
  }
1551
1500
  default:
1552
- if (key && /^image:\d+$/.test(key)) {
1553
- const img = p.images.nodes[parseInt(key.slice(6), 10) - 1] || p.featuredImage;
1554
- return setImage(el, img == null ? void 0 : img.url, (img == null ? void 0 : img.altText) || p.title);
1501
+ if (/^image:\d+$/.test(key)) {
1502
+ const img = p.images.nodes[key.slice(6) - 1] || p.featuredImage;
1503
+ return setImage(el, img?.url, img?.altText || p.title);
1555
1504
  }
1556
- if (key && key.startsWith("metafield:")) {
1557
- const mf = metafieldValue(p, key.slice("metafield:".length));
1505
+ if (key.startsWith("metafield:")) {
1506
+ const mf = metafieldValue(p, key.slice(10));
1558
1507
  if (!mf) return setText(el, "");
1559
- if ((_c = mf.reference) == null ? void 0 : _c.image) return setImage(el, mf.reference.image.url, mf.reference.image.altText);
1560
- if (mf.type === "rich_text_field" || mf.type === "multi_line_text_field") {
1561
- el.textContent = mf.value;
1562
- el.style.whiteSpace = "pre-line";
1563
- return void 0;
1564
- }
1508
+ if (mf.reference?.image) return setImage(el, mf.reference.image.url, mf.reference.image.altText);
1509
+ if (/^(rich_text|multi_line_text)_field$/.test(mf.type)) el.style.whiteSpace = "pre-line";
1565
1510
  return setText(el, mf.value);
1566
1511
  }
1567
1512
  return void 0;
@@ -1572,14 +1517,13 @@ ${cartFragment()}`, { id });
1572
1517
  const opt = p.options.find((o) => o.name === name);
1573
1518
  if (!opt || opt.optionValues.length <= 1 && has(container, "hide-single")) return show(container, false);
1574
1519
  return repeat(container, opt.optionValues, (node, v) => {
1575
- var _a, _b, _c;
1576
1520
  node.setAttribute(attr("option-value"), v.name);
1577
1521
  const t = node.querySelector(sel("bind", "option-value"));
1578
1522
  if (t) setText(t, v.name);
1579
1523
  else if (!node.firstElementChild) setText(node, v.name);
1580
1524
  const sw = node.querySelector(sel("bind", "swatch"));
1581
- if (sw && ((_a = v.swatch) == null ? void 0 : _a.color)) sw.style.backgroundColor = v.swatch.color;
1582
- if (sw && ((_c = (_b = v.swatch) == null ? void 0 : _b.image) == null ? void 0 : _c.previewImage)) setImage(sw, v.swatch.image.previewImage.url, v.name);
1525
+ if (sw && v.swatch?.color) sw.style.backgroundColor = v.swatch.color;
1526
+ if (sw && v.swatch?.image?.previewImage) setImage(sw, v.swatch.image.previewImage.url, v.name);
1583
1527
  });
1584
1528
  });
1585
1529
  this.$(`select${sel("option")}`).forEach((s) => {
@@ -1611,7 +1555,6 @@ ${cartFragment()}`, { id });
1611
1555
  });
1612
1556
  }
1613
1557
  renderVariant() {
1614
- var _a, _b, _c;
1615
1558
  const p = this.product;
1616
1559
  let v = this.findVariant();
1617
1560
  if (!v) {
@@ -1633,63 +1576,40 @@ ${cartFragment()}`, { id });
1633
1576
  const oneTimePrice = v.price;
1634
1577
  const subPrice = this.planId ? planPrice(v, this.planId) : null;
1635
1578
  const price = sub ? subPrice : oneTimePrice;
1636
- const compareAt = sub ? ((_c = (_b = (_a = allocationFor(v, this.planId)) == null ? void 0 : _a.priceAdjustments) == null ? void 0 : _b[0]) == null ? void 0 : _c.compareAtPrice) || v.compareAtPrice : v.compareAtPrice;
1579
+ const compareAt = sub ? allocationFor(v, this.planId)?.priceAdjustments?.[0]?.compareAtPrice || v.compareAtPrice : v.compareAtPrice;
1637
1580
  const onSale = compareAt && parseFloat(compareAt.amount) > parseFloat(price.amount);
1638
1581
  const discountPct = onSale ? Math.round((1 - parseFloat(price.amount) / parseFloat(compareAt.amount)) * 100) : 0;
1639
1582
  const savings = this.planId ? planSavings(v, this.planId) : { amount: null, percentage: 0 };
1640
1583
  const plan = this.plan;
1641
1584
  const total = { amount: (parseFloat(price.amount) * Math.max(1, this.quantity)).toFixed(2), currencyCode: price.currencyCode };
1585
+ const fm = formatMoney;
1586
+ const pct = (n) => n ? `${n}%` : "";
1587
+ const text = {
1588
+ price: () => fm(price),
1589
+ "total-price": () => fm(total),
1590
+ "one-time-price": () => fm(oneTimePrice),
1591
+ "subscription-price": () => fm(subPrice),
1592
+ "subscription-savings": () => pct(savings.percentage),
1593
+ "subscription-savings-amount": () => fm(savings.amount),
1594
+ "checkout-charge": () => fm(sub ? planCheckoutCharge(v, this.planId) : price),
1595
+ "plan-name": () => plan?.name,
1596
+ "plan-description": () => plan?.description,
1597
+ "plan-group": () => plan?.group?.name,
1598
+ "compare-at-price": () => onSale ? fm(compareAt) : "",
1599
+ "unit-price": () => fm(v.unitPrice),
1600
+ "discount-percentage": () => `${discountPct}%`,
1601
+ "discount-amount": () => onSale ? fm({ amount: String(parseFloat(compareAt.amount) - parseFloat(price.amount)), currencyCode: price.currencyCode }) : "",
1602
+ "variant-title": () => v.title,
1603
+ "variant-id": () => v.id,
1604
+ sku: () => v.sku,
1605
+ barcode: () => v.barcode,
1606
+ stock: () => v.quantityAvailable,
1607
+ quantity: () => this.quantity
1608
+ };
1642
1609
  this.$(sel("bind")).forEach((el) => {
1643
- var _a2, _b2, _c2, _d, _e;
1644
- switch (get(el, "bind")) {
1645
- case "price":
1646
- return setText(el, formatMoney(price));
1647
- case "total-price":
1648
- return setText(el, formatMoney(total));
1649
- case "one-time-price":
1650
- return setText(el, formatMoney(oneTimePrice));
1651
- case "subscription-price":
1652
- return setText(el, formatMoney(subPrice));
1653
- case "subscription-savings":
1654
- return setText(el, savings.percentage ? `${savings.percentage}%` : "");
1655
- case "subscription-savings-amount":
1656
- return setText(el, formatMoney(savings.amount));
1657
- case "checkout-charge":
1658
- return setText(el, formatMoney(sub ? planCheckoutCharge(v, this.planId) : price));
1659
- case "plan-name":
1660
- return setText(el, (plan == null ? void 0 : plan.name) || "");
1661
- case "plan-description":
1662
- return setText(el, (plan == null ? void 0 : plan.description) || "");
1663
- case "plan-group":
1664
- return setText(el, ((_a2 = plan == null ? void 0 : plan.group) == null ? void 0 : _a2.name) || "");
1665
- case "compare-at-price":
1666
- return setText(el, onSale ? formatMoney(compareAt) : "");
1667
- case "unit-price":
1668
- return setText(el, formatMoney(v.unitPrice));
1669
- case "discount-percentage":
1670
- return setText(el, `${discountPct}%`);
1671
- case "discount-amount":
1672
- return setText(el, onSale ? formatMoney({
1673
- amount: String(parseFloat(compareAt.amount) - parseFloat(price.amount)),
1674
- currencyCode: price.currencyCode
1675
- }) : "");
1676
- case "variant-title":
1677
- return setText(el, v.title);
1678
- case "variant-id":
1679
- return setText(el, v.id);
1680
- case "sku":
1681
- return setText(el, v.sku);
1682
- case "barcode":
1683
- return setText(el, v.barcode);
1684
- case "stock":
1685
- return setText(el, (_b2 = v.quantityAvailable) != null ? _b2 : "");
1686
- case "quantity":
1687
- return setText(el, this.quantity);
1688
- case "variant-image":
1689
- return setImage(el, ((_c2 = v.image) == null ? void 0 : _c2.url) || ((_d = p.featuredImage) == null ? void 0 : _d.url), ((_e = v.image) == null ? void 0 : _e.altText) || p.title);
1690
- default:
1691
- return void 0;
1692
- }
1610
+ const k = get(el, "bind");
1611
+ if (k === "variant-image") setImage(el, v.image?.url || p.featuredImage?.url, v.image?.altText || p.title);
1612
+ else if (text[k]) setText(el, text[k]());
1693
1613
  });
1694
1614
  this.$(sel("option-selected")).forEach((el) => setText(el, this.selected[get(el, "option-selected")] || ""));
1695
1615
  this.$(sel("option-list")).forEach((container) => {
@@ -1727,23 +1647,16 @@ ${cartFragment()}`, { id });
1727
1647
  qsa(node, sel("plan-bind")).forEach((el) => {
1728
1648
  const adj = planAdjustment(planObj);
1729
1649
  const s = planSavings(v, id);
1730
- switch (get(el, "plan-bind")) {
1731
- case "name":
1732
- return setText(el, planObj == null ? void 0 : planObj.name);
1733
- case "description":
1734
- return setText(el, (planObj == null ? void 0 : planObj.description) || "");
1735
- case "price":
1736
- return setText(el, formatMoney(planPrice(v, id)));
1737
- case "savings":
1738
- return setText(el, s.percentage ? `${s.percentage}%` : "");
1739
- case "savings-amount":
1740
- return setText(el, formatMoney(s.amount));
1741
- case "adjustment":
1742
- if (!adj) return setText(el, "");
1743
- return setText(el, adj.type === "percentage" ? `${adj.value}%` : formatMoney(adj.value));
1744
- default:
1745
- return void 0;
1746
- }
1650
+ const k = get(el, "plan-bind");
1651
+ const val = {
1652
+ name: () => planObj?.name,
1653
+ description: () => planObj?.description,
1654
+ price: () => fm(planPrice(v, id)),
1655
+ savings: () => pct(s.percentage),
1656
+ "savings-amount": () => fm(s.amount),
1657
+ adjustment: () => !adj ? "" : adj.type === "percentage" ? `${adj.value}%` : fm(adj.value)
1658
+ }[k];
1659
+ if (val) setText(el, val());
1747
1660
  });
1748
1661
  });
1749
1662
  this.$(`select${sel("plan")}`).forEach((s) => {
@@ -1782,16 +1695,17 @@ ${cartFragment()}`, { id });
1782
1695
  this.$(sel("add-to-cart")).forEach((btn) => {
1783
1696
  const disabled = !v.availableForSale;
1784
1697
  btn.classList.toggle("is-disabled", disabled);
1698
+ btn.setAttribute("aria-disabled", String(disabled));
1785
1699
  if ("disabled" in btn) btn.disabled = disabled;
1786
1700
  const soldOut = get(btn, "soldout-text");
1787
1701
  const subText = get(btn, "subscribe-text");
1788
1702
  if (soldOut || subText) {
1789
1703
  const label = btn.querySelector(sel("add-label")) || btn;
1790
1704
  if (label.dataset.tetherLabel === void 0) label.dataset.tetherLabel = label.tagName === "INPUT" ? label.value : label.textContent;
1791
- let text = label.dataset.tetherLabel;
1792
- if (sub && subText) text = subText;
1793
- if (disabled && soldOut) text = soldOut;
1794
- if (btn.getAttribute("aria-busy") !== "true") setText(label, text);
1705
+ let text2 = label.dataset.tetherLabel;
1706
+ if (sub && subText) text2 = subText;
1707
+ if (disabled && soldOut) text2 = soldOut;
1708
+ if (btn.getAttribute("aria-busy") !== "true") setText(label, text2);
1795
1709
  }
1796
1710
  });
1797
1711
  emit("variant-changed", {
@@ -1805,6 +1719,11 @@ ${cartFragment()}`, { id });
1805
1719
  };
1806
1720
 
1807
1721
  // src/collection.js
1722
+ var collection_exports = {};
1723
+ __export(collection_exports, {
1724
+ ListScope: () => ListScope,
1725
+ listSelector: () => listSelector
1726
+ });
1808
1727
  var listSelector = () => `${sel("collection")},${sel("products")},${sel("recommendations")}`;
1809
1728
  var ListScope = class {
1810
1729
  constructor(root) {
@@ -1814,8 +1733,7 @@ ${cartFragment()}`, { id });
1814
1733
  this.collection = null;
1815
1734
  setScopeStatus(root, "loading");
1816
1735
  root.addEventListener("click", (e) => {
1817
- var _a, _b;
1818
- const more = (_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, sel("load-more"));
1736
+ const more = e.target.closest?.(sel("load-more"));
1819
1737
  if (more && more.closest(listSelector()) === root) {
1820
1738
  e.preventDefault();
1821
1739
  busy(more, () => this.load(true));
@@ -1837,7 +1755,6 @@ ${cartFragment()}`, { id });
1837
1755
  return opts;
1838
1756
  }
1839
1757
  async load(more = false) {
1840
- var _a;
1841
1758
  const token = this.loadToken = (this.loadToken || 0) + 1;
1842
1759
  const stale = () => token !== this.loadToken;
1843
1760
  try {
@@ -1849,7 +1766,7 @@ ${cartFragment()}`, { id });
1849
1766
  const limit = parseInt(get(this.root, "limit") || "", 10);
1850
1767
  this.products = limit ? recs.slice(0, limit) : recs;
1851
1768
  } else {
1852
- const res = await getProducts({ ...this.options(), after: more ? (_a = this.pageInfo) == null ? void 0 : _a.endCursor : null });
1769
+ const res = await getProducts({ ...this.options(), after: more ? this.pageInfo?.endCursor : null });
1853
1770
  if (stale()) return;
1854
1771
  this.collection = res.collection;
1855
1772
  this.pageInfo = res.pageInfo;
@@ -1868,15 +1785,14 @@ ${cartFragment()}`, { id });
1868
1785
  render() {
1869
1786
  const c = this.collection;
1870
1787
  this.$(sel("collection-bind")).forEach((el) => {
1871
- var _a, _b;
1872
1788
  switch (get(el, "collection-bind")) {
1873
1789
  case "title":
1874
- return setText(el, (c == null ? void 0 : c.title) || "");
1790
+ return setText(el, c?.title || "");
1875
1791
  case "description":
1876
- el.innerHTML = (c == null ? void 0 : c.descriptionHtml) || "";
1792
+ el.innerHTML = c?.descriptionHtml || "";
1877
1793
  return void 0;
1878
1794
  case "image":
1879
- return setImage(el, (_a = c == null ? void 0 : c.image) == null ? void 0 : _a.url, ((_b = c == null ? void 0 : c.image) == null ? void 0 : _b.altText) || (c == null ? void 0 : c.title));
1795
+ return setImage(el, c?.image?.url, c?.image?.altText || c?.title);
1880
1796
  case "count":
1881
1797
  return setText(el, this.products.length);
1882
1798
  default:
@@ -1897,330 +1813,24 @@ ${cartFragment()}`, { id });
1897
1813
  if (k === "list-filled") el.style.display = empty ? "none" : "";
1898
1814
  });
1899
1815
  this.$(sel("load-more")).forEach((el) => {
1900
- var _a;
1901
- el.style.display = ((_a = this.pageInfo) == null ? void 0 : _a.hasNextPage) ? "" : "none";
1816
+ el.style.display = this.pageInfo?.hasNextPage ? "" : "none";
1902
1817
  });
1903
1818
  notifyLayoutChanged("list");
1904
1819
  }
1905
1820
  };
1906
1821
 
1907
- // src/api-version.js
1908
- var parse = (v) => {
1909
- const m = /^(\d{4})-(01|04|07|10)$/.exec(String(v || ""));
1910
- return m ? { y: +m[1], m: +m[2] } : null;
1911
- };
1912
- var fmt = (y, m) => `${y}-${String(m).padStart(2, "0")}`;
1913
- function nextApiVersion(v, steps = 1) {
1914
- const p = parse(v);
1915
- if (!p) throw new Error(`not a quarterly Shopify API version: ${v}`);
1916
- const q = p.y * 4 + (p.m - 1) / 3 + steps;
1917
- return fmt(Math.floor(q / 4), q % 4 * 3 + 1);
1918
- }
1919
- function apiVersionStatus(version, now = /* @__PURE__ */ new Date()) {
1920
- if (version === "unstable") return { version, ok: false, note: '"unstable" changes without notice: pin a dated version such as 2026-07' };
1921
- const p = parse(version);
1922
- if (!p) return { version, ok: false, note: "not a quarterly version (YYYY-01, -04, -07 or -10)" };
1923
- const released = new Date(Date.UTC(p.y, p.m - 1, 1));
1924
- const supportedUntil = new Date(Date.UTC(p.y + 1, p.m - 1, 1));
1925
- const monthsLeft = (supportedUntil - now) / (30.44 * 24 * 3600 * 1e3);
1926
- let note = `supported until about ${supportedUntil.toISOString().slice(0, 7)}`;
1927
- if (released > now) note = "not released yet (release candidate at best)";
1928
- else if (monthsLeft <= 0) note = "no longer supported: Shopify falls back to the oldest supported version. Upgrade (see README)";
1929
- else if (monthsLeft < 3) note = `${note}: plan the upgrade to ${nextApiVersion(version)} or later (see README)`;
1930
- return { version, ok: released <= now && monthsLeft > 0, monthsLeft: Math.round(monthsLeft), next: nextApiVersion(version), note };
1931
- }
1932
-
1933
- // src/markets.js
1934
- var COUNTRY = /^[A-Z]{2}$/;
1935
- var REGIONAL_LANGUAGES = /* @__PURE__ */ new Set(["PT_BR", "PT_PT", "ZH_CN", "ZH_TW"]);
1936
- var storageKey = () => `${cfg.prefix}:market:${cfg.domain}`;
1937
- var defaultEnv = () => ({
1938
- document: typeof document !== "undefined" ? document : null,
1939
- location: typeof window !== "undefined" ? window.location : null,
1940
- storage: (() => {
1941
- try {
1942
- return window.localStorage;
1943
- } catch {
1944
- return null;
1945
- }
1946
- })(),
1947
- global: typeof window !== "undefined" ? window : {}
1822
+ // src/mounting.js
1823
+ var mounting_exports = {};
1824
+ __export(mounting_exports, {
1825
+ liveLists: () => liveLists,
1826
+ liveProducts: () => liveProducts,
1827
+ mountProduct: () => mountProduct,
1828
+ mountedLists: () => mountedLists,
1829
+ mountedProducts: () => mountedProducts,
1830
+ pruneLive: () => pruneLive,
1831
+ retryFailed: () => retryFailed,
1832
+ within: () => within
1948
1833
  });
1949
- function toCountry(v) {
1950
- const c = String(v || "").trim().toUpperCase();
1951
- return COUNTRY.test(c) && c !== "XX" && c !== "T1" ? c : null;
1952
- }
1953
- function toLanguage(v) {
1954
- const raw = String(v || "").trim().replace("-", "_").toUpperCase();
1955
- if (!raw) return null;
1956
- if (REGIONAL_LANGUAGES.has(raw)) return raw;
1957
- const primary = raw.split("_")[0];
1958
- return /^[A-Z]{2,3}$/.test(primary) ? primary : null;
1959
- }
1960
- function readCookie(doc, name) {
1961
- if (!doc || !name) return null;
1962
- const hit = String(doc.cookie || "").split(";").map((c) => c.trim()).find((c) => c.startsWith(`${name}=`));
1963
- return hit ? decodeURIComponent(hit.slice(name.length + 1)) : null;
1964
- }
1965
- var lookup = (locales, key) => {
1966
- if (!locales || !key) return null;
1967
- const k = String(key).toLowerCase();
1968
- const found = Object.keys(locales).find((x) => x.toLowerCase() === k);
1969
- return found ? locales[found] : null;
1970
- };
1971
- var readers = {
1972
- saved(m, env) {
1973
- if (m.remember === false || !env.storage) return null;
1974
- try {
1975
- return JSON.parse(env.storage.getItem(storageKey()) || "null");
1976
- } catch {
1977
- return null;
1978
- }
1979
- },
1980
- server(m, env) {
1981
- const meta = (name) => {
1982
- var _a, _b, _c;
1983
- return (_c = (_b = (_a = env.document) == null ? void 0 : _a.querySelector) == null ? void 0 : _b.call(_a, `meta[name="${name}"]`)) == null ? void 0 : _c.getAttribute("content");
1984
- };
1985
- const country = env.global.TETHER_COUNTRY || meta("tether-country") || readCookie(env.document, m.cookie || "tether_country");
1986
- const language = env.global.TETHER_LANGUAGE || meta("tether-language");
1987
- return { country, language };
1988
- },
1989
- webflow(m, env) {
1990
- var _a, _b, _c;
1991
- const segment = (((_a = env.location) == null ? void 0 : _a.pathname) || "/").split("/").filter(Boolean)[0];
1992
- const fromPath = lookup(m.locales, segment);
1993
- if (fromPath) return fromPath;
1994
- const lang = (_c = (_b = env.document) == null ? void 0 : _b.documentElement) == null ? void 0 : _c.getAttribute("lang");
1995
- if (!lang) return null;
1996
- const mapped = lookup(m.locales, lang) || lookup(m.locales, lang.split("-")[0]);
1997
- if (mapped) return { locale: lang, ...mapped };
1998
- if (m.locales && m.strict) return null;
1999
- const region = lang.split("-")[1];
2000
- return { language: lang, country: region && region.length === 2 ? region : null, locale: lang };
2001
- }
2002
- };
2003
- function detectMarket(markets = cfg.markets, env = defaultEnv()) {
2004
- const m = markets || {};
2005
- if (m.detect === false) return {};
2006
- const list = !m.detect ? ["saved"] : m.detect === true ? ["saved", "server", "webflow"] : [].concat(m.detect);
2007
- markets = m;
2008
- const out = { sources: {} };
2009
- list.forEach((name) => {
2010
- var _a;
2011
- const r = (_a = readers[name]) == null ? void 0 : _a.call(readers, markets, env);
2012
- if (!r) return;
2013
- const vals = { country: toCountry(r.country), language: toLanguage(r.language), locale: r.locale || null };
2014
- Object.entries(vals).forEach(([k, v]) => {
2015
- if (v && !out[k]) {
2016
- out[k] = v;
2017
- out.sources[k] = name;
2018
- }
2019
- });
2020
- });
2021
- return out;
2022
- }
2023
- function rememberMarket(market) {
2024
- var _a;
2025
- if (((_a = cfg.markets) == null ? void 0 : _a.remember) === false) return;
2026
- try {
2027
- localStorage.setItem(storageKey(), JSON.stringify({ country: market.country, language: market.language || void 0 }));
2028
- } catch {
2029
- }
2030
- }
2031
- var localizationPromise = null;
2032
- function clearLocalization() {
2033
- localizationPromise = null;
2034
- }
2035
- function getLocalization() {
2036
- if (!localizationPromise) {
2037
- localizationPromise = gql(`query TetherLocalization {
2038
- localization {
2039
- country { isoCode name currency { isoCode symbol } }
2040
- language { isoCode name }
2041
- availableCountries { isoCode name currency { isoCode symbol } availableLanguages { isoCode name } }
2042
- }
2043
- }`).then((d) => d.localization).catch((err) => {
2044
- localizationPromise = null;
2045
- throw err;
2046
- });
2047
- }
2048
- return localizationPromise;
2049
- }
2050
-
2051
- // src/diagnose.js
2052
- var mask = (t) => t ? `${String(t).slice(0, 4)}\u2026${String(t).slice(-4)} (${String(t).length} chars)` : "(missing)";
2053
- function describe(el) {
2054
- const id = el.id ? `#${el.id}` : "";
2055
- const cls = el.className && typeof el.className === "string" ? `.${el.className.trim().split(/\s+/).slice(0, 2).join(".")}` : "";
2056
- return `<${el.tagName.toLowerCase()}${id}${cls}>`;
2057
- }
2058
- function checkMarkup(root = document) {
2059
- const problems = [];
2060
- const add = (level, message, el) => problems.push({ level, message, element: el ? describe(el) : void 0, el });
2061
- const inProduct = (el) => Boolean(el.closest(sel("product")));
2062
- const listSel = `${sel("collection")},${sel("products")},${sel("recommendations")}`;
2063
- qsa(root, sel("product")).forEach((el) => {
2064
- if (el.closest(listSel) && el.closest(sel("list"))) return;
2065
- const { key, fromUrl, raw } = resolveProductKey(el);
2066
- if (!raw) add("error", `${attr("product")} has no value. Bind it to the Shopify handle (CMS field), or use "url:handle".`, el);
2067
- else if (fromUrl && !key) add("warn", `${attr("product")}="${raw}" but this URL has no value for it (e.g. ?handle=my-product).`, el);
2068
- });
2069
- const productOnly = ["bind", "option-list", "option", "variant-select", "add-to-cart", "purchase", "plan-list", "plan", "qty", "line-attribute"];
2070
- productOnly.forEach((name) => {
2071
- qsa(root, sel(name)).forEach((el) => {
2072
- if (inProduct(el)) return;
2073
- if (name === "line-attribute" && el.closest(sel("group"))) return;
2074
- add("warn", `${attr(name)} outside any ${attr("product")}: it does nothing here.`, el);
2075
- });
2076
- });
2077
- qsa(root, sel("group-add")).forEach((el) => {
2078
- if (!el.closest(sel("group"))) add("warn", `${attr("group-add")} outside a ${attr("group")}.`, el);
2079
- });
2080
- [...qsa(root, sel("option-list")), ...qsa(root, sel("plan-list")), ...qsa(root, sel("cart-lines")), ...qsa(root, sel("list"))].forEach((el) => {
2081
- if (!el.firstElementChild && !hasTemplate(el)) {
2082
- add("warn", "Repeater without a template child: put one example element inside it.", el);
2083
- }
2084
- });
2085
- qsa(root, listSel).forEach((el) => {
2086
- if (!qsa(el, sel("list")).length) add("error", `List without a ${attr("list")} child: products have nowhere to render.`, el);
2087
- });
2088
- const known = /* @__PURE__ */ new Set([
2089
- "in-stock",
2090
- "out-of-stock",
2091
- "backorder",
2092
- "low-stock",
2093
- "on-sale",
2094
- "not-on-sale",
2095
- "has-variants",
2096
- "single-variant",
2097
- "subscription-available",
2098
- "subscription-unavailable",
2099
- "subscription-only",
2100
- "one-time-available",
2101
- "subscription-selected",
2102
- "one-time-selected",
2103
- "has-savings",
2104
- "loading",
2105
- "ready",
2106
- "error",
2107
- "not-found",
2108
- "cart-empty",
2109
- "cart-filled",
2110
- "cart-has-subscription",
2111
- "cart-no-subscription",
2112
- "cart-has-discount",
2113
- "cart-error",
2114
- "list-empty",
2115
- "list-filled"
2116
- ]);
2117
- qsa(root, sel("show")).forEach((el) => {
2118
- const k = get(el, "show");
2119
- if (!known.has(k)) add("warn", `Unknown ${attr("show")}="${k}".`, el);
2120
- });
2121
- qsa(root, sel("bind")).forEach((el) => {
2122
- const k = get(el, "bind") || "";
2123
- if (!k.startsWith("metafield:")) return;
2124
- const ref = k.slice(10);
2125
- const listed = (cfg.metafields || []).some((m) => (typeof m === "string" ? m : `${m.namespace}.${m.key}`) === ref);
2126
- if (!listed) add("error", `${attr("bind")}="${k}" but "${ref}" is not in config.metafields, so it stays empty.`, el);
2127
- });
2128
- if (qsa(root, sel("cart-open")).length && !qsa(root, sel("cart-drawer")).length) {
2129
- add("warn", `${attr("cart-open")} exists but there is no ${attr("cart-drawer")} on this page.`);
2130
- }
2131
- if (qsa(root, sel("cart-drawer")).length > 1) add("warn", `More than one ${attr("cart-drawer")}.`);
2132
- qsa(root, `[${attr("state")}="error"]`).forEach((el) => {
2133
- if (el.matches(boundary())) add("error", `Scope failed to load (${get(el, "error") || "error"}).`, el);
2134
- });
2135
- return problems;
2136
- }
2137
- async function diagnose({ root = document, silent = false, version, now = /* @__PURE__ */ new Date() } = {}) {
2138
- var _a, _b, _c, _d, _e, _f, _g;
2139
- const report = {
2140
- version,
2141
- config: {
2142
- domain: cfg.domain || "(missing)",
2143
- token: mask(cfg.token),
2144
- apiVersion: cfg.apiVersion,
2145
- endpoint: cfg.domain ? endpoint() : null,
2146
- country: cfg.country,
2147
- language: cfg.language,
2148
- locale: cfg.locale,
2149
- markets: cfg.markets || null,
2150
- prefix: cfg.prefix,
2151
- productPath: cfg.productPath,
2152
- metafields: cfg.metafields,
2153
- autoMount: cfg.autoMount,
2154
- observe: cfg.observe,
2155
- onError: typeof cfg.onError === "function"
2156
- },
2157
- apiVersion: apiVersionStatus(cfg.apiVersion, now),
2158
- market: ((_a = cfg.markets) == null ? void 0 : _a.detect) ? detectMarket(cfg.markets) : null,
2159
- reachable: false,
2160
- shop: null,
2161
- latencyMs: null,
2162
- products: [],
2163
- sellingPlans: [],
2164
- problems: []
2165
- };
2166
- if (!isConfigured()) {
2167
- report.problems.push({ level: "error", message: "TETHER_CONFIG needs domain and token." });
2168
- } else {
2169
- const t0 = Date.now();
2170
- try {
2171
- const data = await gql("query TetherDiagnose { shop { name primaryDomain { url } paymentSettings { currencyCode } } }", {}, { context: false });
2172
- report.reachable = true;
2173
- report.shop = { name: (_b = data.shop) == null ? void 0 : _b.name, url: (_d = (_c = data.shop) == null ? void 0 : _c.primaryDomain) == null ? void 0 : _d.url, currency: (_f = (_e = data.shop) == null ? void 0 : _e.paymentSettings) == null ? void 0 : _f.currencyCode };
2174
- } catch (err) {
2175
- const hint = err.status === 401 || err.status === 403 ? "Token rejected: use the public Storefront token from the Headless channel, not an Admin token." : err.status === 404 ? "Store or API version not found: check domain (x.myshopify.com) and apiVersion." : "";
2176
- report.problems.push({ level: "error", message: `Storefront API not reachable: ${err.message}${hint ? `. ${hint}` : ""}` });
2177
- }
2178
- report.latencyMs = Date.now() - t0;
2179
- if (report.reachable) {
2180
- const keys = [...new Set(qsa(root, sel("product")).map((el) => resolveProductKey(el).key).filter(Boolean))].slice(0, 20);
2181
- const results = await Promise.allSettled(keys.map((k) => getProduct(k)));
2182
- results.forEach((r, i) => {
2183
- var _a2, _b2;
2184
- if (r.status === "fulfilled") {
2185
- const p = r.value;
2186
- const plans = allPlans(p);
2187
- report.products.push({ key: keys[i], handle: p.handle, title: p.title, plans: plans.length, requiresSellingPlan: p.requiresSellingPlan });
2188
- plans.forEach((pl) => {
2189
- var _a3;
2190
- return report.sellingPlans.push({ product: p.handle, group: (_a3 = pl.group) == null ? void 0 : _a3.name, plan: pl.name, id: pl.id });
2191
- });
2192
- } else {
2193
- report.products.push({ key: keys[i], error: (_a2 = r.reason) == null ? void 0 : _a2.message });
2194
- report.problems.push({ level: "error", message: `Product "${keys[i]}": ${(_b2 = r.reason) == null ? void 0 : _b2.message}. Is it published to the Headless channel?` });
2195
- }
2196
- });
2197
- }
2198
- }
2199
- if (!report.apiVersion.ok || ((_g = report.apiVersion.monthsLeft) != null ? _g : 99) < 3) {
2200
- report.problems.push({ level: report.apiVersion.ok ? "warn" : "error", message: `apiVersion ${cfg.apiVersion}: ${report.apiVersion.note}` });
2201
- }
2202
- if (typeof document !== "undefined") report.problems.push(...checkMarkup(root));
2203
- if (!silent) print(report);
2204
- return report;
2205
- }
2206
- function print(r) {
2207
- var _a, _b, _c;
2208
- const c = console;
2209
- const errors = r.problems.filter((p) => p.level === "error").length;
2210
- (_a = c.group) == null ? void 0 : _a.call(c, `[${cfg.prefix}] diagnose: ${errors ? `${errors} error(s)` : "no errors"}, ${r.problems.length - errors} warning(s)`);
2211
- c.log("version", r.version, "| API", r.apiVersion.version, `(${r.apiVersion.note})`);
2212
- c.log("config", r.config);
2213
- if (r.market) c.log("market detected", r.market, "(in use:", r.config.country, r.config.language, ")");
2214
- c.log(r.reachable ? `Storefront API reachable: ${(_b = r.shop) == null ? void 0 : _b.name} (${r.latencyMs} ms)` : "Storefront API NOT reachable");
2215
- if (r.products.length) (c.table || c.log)(r.products);
2216
- if (r.sellingPlans.length) (c.table || c.log)(r.sellingPlans);
2217
- else if (r.reachable) c.log("No selling plans on the products of this page.");
2218
- r.problems.forEach((p) => (p.level === "error" ? c.error : c.warn)(p.message, p.el || ""));
2219
- (_c = c.groupEnd) == null ? void 0 : _c.call(c);
2220
- }
2221
-
2222
- // src/index.js
2223
- var VERSION = "0.3.5";
2224
1834
  var mountedProducts = /* @__PURE__ */ new WeakSet();
2225
1835
  var mountedLists = /* @__PURE__ */ new WeakSet();
2226
1836
  var liveProducts = /* @__PURE__ */ new Set();
@@ -2234,35 +1844,8 @@ ${cartFragment()}`, { id });
2234
1844
  if (!l.root.isConnected) liveLists.delete(l);
2235
1845
  });
2236
1846
  }
2237
- var initPromise = null;
2238
- var observer = null;
2239
1847
  function within(root, selector) {
2240
- var _a;
2241
- return [...((_a = root.matches) == null ? void 0 : _a.call(root, selector)) ? [root] : [], ...qsa(root, selector)];
2242
- }
2243
- function mount(root = document) {
2244
- if (!isConfigured()) return 0;
2245
- let count = 0;
2246
- pruneLive();
2247
- within(root, listSelector()).forEach((el) => {
2248
- if (mountedLists.has(el)) return;
2249
- mountedLists.add(el);
2250
- count++;
2251
- const list = new ListScope(el);
2252
- liveLists.add(list);
2253
- el.addEventListener(`${cfg.prefix}:list-rendered`, (e) => mount(e.detail.container));
2254
- list.load();
2255
- });
2256
- within(root, sel("product")).forEach((el) => {
2257
- if (mountProduct(el)) count++;
2258
- });
2259
- renderCountrySelects(root);
2260
- qsa(root, sel("account-link")).forEach((a) => {
2261
- a.setAttribute("href", `https://${cfg.domain}/account`);
2262
- });
2263
- Cart.render();
2264
- log(`mounted ${count} scope(s)`, root);
2265
- return count;
1848
+ return [...root.matches?.(selector) ? [root] : [], ...qsa(root, selector)];
2266
1849
  }
2267
1850
  function mountProduct(el) {
2268
1851
  if (mountedProducts.has(el)) return false;
@@ -2305,74 +1888,91 @@ ${cartFragment()}`, { id });
2305
1888
  });
2306
1889
  return true;
2307
1890
  }
2308
- function renderCountrySelects(root = document) {
2309
- const selects = within(root, `select${sel("country-select")}`);
2310
- const binds = within(root, sel("country-bind"));
2311
- if (!selects.length && !binds.length) return Promise.resolve();
2312
- return getLocalization().then((loc) => {
2313
- var _a;
2314
- const current = (cfg.country || ((_a = loc.country) == null ? void 0 : _a.isoCode) || "").toUpperCase();
2315
- const countries = [...loc.availableCountries || []].sort((a, b) => a.name.localeCompare(b.name, cfg.locale || void 0));
2316
- const info = countries.find((c) => c.isoCode === current) || loc.country;
2317
- const fill = (tpl, c) => {
2318
- var _a2, _b;
2319
- return tpl.replace(/\{name\}/g, c.name).replace(/\{code\}/g, c.isoCode).replace(/\{currency\}/g, ((_a2 = c.currency) == null ? void 0 : _a2.isoCode) || "").replace(/\{symbol\}/g, ((_b = c.currency) == null ? void 0 : _b.symbol) || "");
2320
- };
2321
- selects.forEach((s) => {
2322
- const tpl = s.getAttribute(attr("country-label")) || "{name} ({currency})";
2323
- s.innerHTML = "";
2324
- countries.forEach((c) => s.appendChild(new Option(fill(tpl, c), c.isoCode)));
2325
- s.value = current;
2326
- if (s.value !== current) s.selectedIndex = -1;
2327
- s.disabled = countries.length < 2 && !s.hasAttribute(attr("always-enabled"));
2328
- });
2329
- binds.forEach((el) => {
2330
- var _a2, _b;
2331
- const k = get(el, "country-bind") || "name";
2332
- const v = { name: info == null ? void 0 : info.name, code: info == null ? void 0 : info.isoCode, currency: (_a2 = info == null ? void 0 : info.currency) == null ? void 0 : _a2.isoCode, symbol: (_b = info == null ? void 0 : info.currency) == null ? void 0 : _b.symbol }[k];
2333
- el.textContent = v || "";
2334
- });
2335
- }).catch((err) => reportError(err, "country select"));
2336
- }
2337
- async function setCountry(code, { language, remember = true } = {}) {
2338
- const country = toCountry(code);
2339
- if (!country) throw new TetherError(`Not a country code: ${code}`, { code: "INVALID_COUNTRY" });
2340
- const lang = language === void 0 ? cfg.language : toLanguage(language);
2341
- const changed = country !== String(cfg.country || "").toUpperCase() || lang !== cfg.language;
2342
- cfg.country = country;
2343
- cfg.language = lang || void 0;
2344
- if (remember) rememberMarket({ country, language: lang });
2345
- if (!changed) {
2346
- renderCountrySelects();
2347
- return Cart.state;
2348
- }
2349
- clearProductCache();
2350
- const jobs = [];
2351
- if (language !== void 0) clearLocalization();
2352
- if (Cart.id()) jobs.push(Cart.setBuyerIdentity({ countryCode: country }, { create: false }).catch((err) => reportError(err, "cart country")));
1891
+ function applyPageTitle(el, p) {
1892
+ const tpl = get(el, "page-title");
1893
+ if (!tpl || typeof document === "undefined") return;
1894
+ document.title = tpl.replace(/\{title\}/g, p.title).replace(/\{vendor\}/g, p.vendor || "").replace(/\{type\}/g, p.productType || "");
1895
+ }
1896
+ function retryFailed() {
2353
1897
  pruneLive();
2354
- liveLists.forEach((list) => jobs.push(list.load()));
2355
1898
  liveProducts.forEach((el) => {
2356
- if (el.closest(listSelector())) return;
1899
+ if (el.getAttribute(attr("state")) !== "error" || el.getAttribute(attr("error")) === "not-found") return;
2357
1900
  mountedProducts.delete(el);
2358
1901
  mountProduct(el);
2359
1902
  });
2360
- renderCountrySelects();
2361
- emit("country-changed", { country, language: cfg.language });
2362
- await Promise.all(jobs);
2363
- return Cart.state;
2364
1903
  }
2365
- function applyPageTitle(el, p) {
2366
- const tpl = get(el, "page-title");
2367
- if (!tpl || typeof document === "undefined") return;
2368
- document.title = tpl.replace(/\{title\}/g, p.title).replace(/\{vendor\}/g, p.vendor || "").replace(/\{type\}/g, p.productType || "");
1904
+
1905
+ // src/lazy-iife.js
1906
+ var { cfg: cfg2 } = config_exports;
1907
+ var coreModules = {
1908
+ "config.js": config_exports,
1909
+ "client.js": client_exports,
1910
+ "dom.js": dom_exports,
1911
+ "fragments.js": fragments_exports,
1912
+ "loader.js": loader_exports,
1913
+ "selling-plans.js": selling_plans_exports,
1914
+ "status.js": status_exports,
1915
+ "cart.js": cart_exports,
1916
+ "product.js": product_exports,
1917
+ "collection.js": collection_exports,
1918
+ "mounting.js": mounting_exports
1919
+ };
1920
+ var self = typeof document !== "undefined" ? document.currentScript?.src || "" : "";
1921
+ var parts = {};
1922
+ function loadPart(name) {
1923
+ if (parts[name]) return parts[name];
1924
+ const base = cfg2.partsUrl || self.replace(/[^/]*$/, "");
1925
+ const min = /\.min\.js(\?|$)/.test(self) || !self ? ".min" : "";
1926
+ const url = `${base.replace(/\/?$/, "/")}tether.${name}${min}.js`;
1927
+ parts[name] = new Promise((resolve, reject) => {
1928
+ const s = document.createElement("script");
1929
+ s.src = url;
1930
+ s.async = true;
1931
+ s.onload = () => window.Tether?.__parts?.[name] ? resolve(window.Tether.__parts[name]) : reject(new Error(`${url} did not register`));
1932
+ s.onerror = () => {
1933
+ delete parts[name];
1934
+ reject(new Error(`Could not load ${url}. Host it next to the Tether script, or set partsUrl.`));
1935
+ };
1936
+ document.head.appendChild(s);
1937
+ });
1938
+ return parts[name];
1939
+ }
1940
+
1941
+ // src/index.js
1942
+ var VERSION = "0.4.0";
1943
+ var initPromise = null;
1944
+ var observer = null;
1945
+ function mount(root = document) {
1946
+ if (!isConfigured()) return 0;
1947
+ let count = 0;
1948
+ pruneLive();
1949
+ within(root, listSelector()).forEach((el) => {
1950
+ if (mountedLists.has(el)) return;
1951
+ mountedLists.add(el);
1952
+ count++;
1953
+ const list = new ListScope(el);
1954
+ liveLists.add(list);
1955
+ el.addEventListener(`${cfg.prefix}:list-rendered`, (e) => mount(e.detail.container));
1956
+ list.load();
1957
+ });
1958
+ within(root, sel("product")).forEach((el) => {
1959
+ if (mountProduct(el)) count++;
1960
+ });
1961
+ if (within(root, `${sel("country-select")},${sel("country-bind")}`).length) {
1962
+ loadPart("markets").then((m) => m.renderCountrySelects(root)).catch((err) => reportError(err, "markets"));
1963
+ }
1964
+ qsa(root, sel("account-link")).forEach((a) => {
1965
+ a.setAttribute("href", `https://${cfg.domain}/account`);
1966
+ });
1967
+ Cart.render();
1968
+ log(`mounted ${count} scope(s)`, root);
1969
+ return count;
2369
1970
  }
2370
1971
  function unmount() {
2371
1972
  Cart.close();
2372
1973
  }
2373
1974
  function getScope(el) {
2374
- var _a;
2375
- const root = (_a = el == null ? void 0 : el.closest) == null ? void 0 : _a.call(el, sel("product"));
1975
+ const root = el?.closest?.(sel("product"));
2376
1976
  return root ? scopes.get(root) || null : null;
2377
1977
  }
2378
1978
  var controlsBound = false;
@@ -2380,9 +1980,8 @@ ${cartFragment()}`, { id });
2380
1980
  if (controlsBound) return;
2381
1981
  controlsBound = true;
2382
1982
  document.addEventListener("click", (e) => {
2383
- var _a;
2384
1983
  const t = e.target;
2385
- if (!(t == null ? void 0 : t.closest)) return;
1984
+ if (!t?.closest) return;
2386
1985
  if (t.closest(sel("cart-open"))) {
2387
1986
  e.preventDefault();
2388
1987
  Cart.toggle();
@@ -2420,7 +2019,7 @@ ${cartFragment()}`, { id });
2420
2019
  const removeCode = t.closest(sel("discount-remove"));
2421
2020
  if (removeCode) {
2422
2021
  e.preventDefault();
2423
- const code = (_a = removeCode.closest(sel("discount-code"))) == null ? void 0 : _a.getAttribute(attr("discount-code"));
2022
+ const code = removeCode.closest(sel("discount-code"))?.getAttribute(attr("discount-code"));
2424
2023
  if (code) Cart.removeDiscount(code).catch((err) => reportError(err, "discount"));
2425
2024
  return;
2426
2025
  }
@@ -2432,9 +2031,9 @@ ${cartFragment()}`, { id });
2432
2031
  });
2433
2032
  document.addEventListener("change", (e) => {
2434
2033
  const t = e.target;
2435
- if (!(t == null ? void 0 : t.closest)) return;
2034
+ if (!t?.closest) return;
2436
2035
  const lineEl = t.closest(dataKeySel());
2437
- const id = lineEl == null ? void 0 : lineEl.getAttribute(`data-${cfg.prefix}-key`);
2036
+ const id = lineEl?.getAttribute(`data-${cfg.prefix}-key`);
2438
2037
  if (id && t.matches(`input${sel("line", "quantity")}`)) {
2439
2038
  Cart.updateLine(id, parseInt(t.value, 10) || 0).catch((err) => reportError(err, "cart line"));
2440
2039
  } else if (id && t.matches(`select${sel("line-plan")}`)) {
@@ -2446,18 +2045,16 @@ ${cartFragment()}`, { id });
2446
2045
  }
2447
2046
  });
2448
2047
  document.addEventListener("submit", (e) => {
2449
- var _a, _b;
2450
- const form = (_b = (_a = e.target).closest) == null ? void 0 : _b.call(_a, sel("discount-form"));
2048
+ const form = e.target.closest?.(sel("discount-form"));
2451
2049
  if (!form) return;
2452
2050
  e.preventDefault();
2453
2051
  const input = form.querySelector(sel("discount-input")) || form.querySelector("input");
2454
- const code = input == null ? void 0 : input.value.trim();
2052
+ const code = input?.value.trim();
2455
2053
  if (!code) return;
2456
2054
  busy(form, async () => {
2457
- var _a2;
2458
2055
  try {
2459
2056
  const cart = await Cart.applyDiscount(code);
2460
- const ok = (_a2 = ((cart == null ? void 0 : cart.discountCodes) || []).find((d) => d.code.toLowerCase() === code.toLowerCase())) == null ? void 0 : _a2.applicable;
2057
+ const ok = (cart?.discountCodes || []).find((d) => d.code.toLowerCase() === code.toLowerCase())?.applicable;
2461
2058
  form.classList.toggle("is-invalid", !ok);
2462
2059
  emit(ok ? "discount-applied" : "discount-invalid", { code }, form);
2463
2060
  if (ok && input) input.value = "";
@@ -2467,8 +2064,28 @@ ${cartFragment()}`, { id });
2467
2064
  }
2468
2065
  });
2469
2066
  });
2067
+ window.addEventListener("online", () => {
2068
+ retryFailed();
2069
+ liveLists.forEach((l) => {
2070
+ if (l.root.getAttribute(attr("state")) === "error") l.load();
2071
+ });
2072
+ });
2470
2073
  document.addEventListener("keydown", (e) => {
2471
- if (e.key === "Escape" && Cart.isOpen()) Cart.close();
2074
+ if (!Cart.isOpen()) return;
2075
+ if (e.key === "Escape") {
2076
+ Cart.close();
2077
+ return;
2078
+ }
2079
+ if (e.key !== "Tab" || cfg.cartFocus === false) return;
2080
+ const d = Cart.drawers().find((x) => x.classList.contains(cfg.cartDrawerOpenClass));
2081
+ const f = d ? focusables(d) : [];
2082
+ if (!f.length) return;
2083
+ const first = f[0];
2084
+ const last = f[f.length - 1];
2085
+ if (!d.contains(document.activeElement) || e.shiftKey && document.activeElement === first || !e.shiftKey && document.activeElement === last) {
2086
+ e.preventDefault();
2087
+ (e.shiftKey ? last : first).focus();
2088
+ }
2472
2089
  });
2473
2090
  }
2474
2091
  function dataKeySel() {
@@ -2480,10 +2097,7 @@ ${cartFragment()}`, { id });
2480
2097
  const name = get(group, "group");
2481
2098
  const extra = toAttributes(parseJSONAttr(group, "line-attributes"));
2482
2099
  const groupAttrs = name ? [{ key: "_group", value: name }, ...extra] : extra;
2483
- const lines = qsa(group, sel("product")).map((el) => scopes.get(el)).filter((s) => {
2484
- var _a;
2485
- return s && ((_a = s.variant) == null ? void 0 : _a.availableForSale) && s.quantity > 0;
2486
- }).map((s) => {
2100
+ const lines = qsa(group, sel("product")).map((el) => scopes.get(el)).filter((s) => s && s.variant?.availableForSale && s.quantity > 0).map((s) => {
2487
2101
  const l = s.getLine();
2488
2102
  l.attributes = [...l.attributes || [], ...groupAttrs];
2489
2103
  return l;
@@ -2502,7 +2116,6 @@ ${cartFragment()}`, { id });
2502
2116
  }
2503
2117
  }
2504
2118
  function init(overrides = {}) {
2505
- var _a;
2506
2119
  if (initPromise && !Object.keys(overrides).length) return initPromise;
2507
2120
  configure({ ...window.TETHER_CONFIG || {}, ...overrides });
2508
2121
  if (!isConfigured()) {
@@ -2510,17 +2123,11 @@ ${cartFragment()}`, { id });
2510
2123
  initPromise = Promise.resolve(null);
2511
2124
  return initPromise;
2512
2125
  }
2513
- const market = detectMarket(cfg.markets);
2514
- if (market.country) cfg.country = market.country;
2515
- if (market.language) cfg.language = market.language;
2516
- if (market.locale && !overrides.locale && !((_a = window.TETHER_CONFIG) == null ? void 0 : _a.locale)) cfg.locale = market.locale;
2517
- if (market.sources && Object.keys(market.sources).length) log("market", market);
2518
2126
  bindControls();
2519
2127
  if (cfg.observe) startObserver();
2520
- initPromise = Cart.init().then(async (cart) => {
2521
- var _a2;
2128
+ initPromise = applyMarket(overrides).then(() => Cart.init()).then(async (cart) => {
2522
2129
  const want = cfg.country ? String(cfg.country).toUpperCase() : null;
2523
- if (cart && want && ((_a2 = cart.buyerIdentity) == null ? void 0 : _a2.countryCode) && cart.buyerIdentity.countryCode !== want) {
2130
+ if (cart && want && cart.buyerIdentity?.countryCode && cart.buyerIdentity.countryCode !== want) {
2524
2131
  try {
2525
2132
  cart = await Cart.setBuyerIdentity({ countryCode: want }, { create: false });
2526
2133
  } catch (err) {
@@ -2532,6 +2139,33 @@ ${cartFragment()}`, { id });
2532
2139
  });
2533
2140
  return initPromise;
2534
2141
  }
2142
+ async function applyMarket(overrides) {
2143
+ const m = cfg.markets || {};
2144
+ if (m.detect === false) return;
2145
+ let market = null;
2146
+ if (m.detect) {
2147
+ try {
2148
+ market = (await loadPart("markets")).detectMarket(m);
2149
+ } catch (err) {
2150
+ reportError(err, "markets");
2151
+ return;
2152
+ }
2153
+ } else if (m.remember !== false) {
2154
+ try {
2155
+ market = JSON.parse(localStorage.getItem(`${cfg.prefix}:market:${cfg.domain}`) || "null");
2156
+ } catch {
2157
+ }
2158
+ if (market && !/^[A-Z]{2}$/.test(market.country || "")) market = null;
2159
+ }
2160
+ if (!market) return;
2161
+ if (market.country) cfg.country = market.country;
2162
+ if (market.language) cfg.language = market.language;
2163
+ if (market.locale && !overrides.locale && !window.TETHER_CONFIG?.locale) cfg.locale = market.locale;
2164
+ log("market", market);
2165
+ }
2166
+ function setCountry(code, opts) {
2167
+ return loadPart("markets").then((m) => m.setCountry(code, opts));
2168
+ }
2535
2169
  function startObserver() {
2536
2170
  if (observer || typeof MutationObserver === "undefined") return;
2537
2171
  let t = null;
@@ -2577,9 +2211,11 @@ ${cartFragment()}`, { id });
2577
2211
  TetherError,
2578
2212
  resolveProductKey,
2579
2213
  setCountry,
2580
- detectMarket: (markets) => detectMarket(markets || cfg.markets),
2581
- getLocalization,
2582
- diagnose: (opts) => diagnose({ ...opts, version: VERSION }),
2214
+ detectMarket: (markets) => loadPart("markets").then((m) => m.detectMarket(markets || cfg.markets)),
2215
+ getLocalization: () => loadPart("markets").then((m) => m.getLocalization()),
2216
+ diagnose: (opts) => loadPart("diagnose").then((m) => m.diagnose({ ...opts, version: VERSION })),
2217
+ __core: coreModules,
2218
+ __parts: {},
2583
2219
  on(name, fn) {
2584
2220
  const h = (e) => fn(e.detail, e);
2585
2221
  document.addEventListener(`${cfg.prefix}:${name}`, h);