@occluze/tether 0.7.1 → 0.7.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +51 -109
  2. package/dist/esm/astro.js +1 -1
  3. package/dist/esm/chunks/{account-CXMA5RPZ.js → account-W4FBRKXA.js} +1 -1
  4. package/dist/esm/chunks/{account-pages-IZO7FEOZ.js → account-pages-IM3OQTOP.js} +1 -1
  5. package/dist/esm/chunks/analytics-QDVOLZY3.js +1 -0
  6. package/dist/esm/chunks/{chunk-BBVUVPMO.js → chunk-AUVAN3KZ.js} +1 -1
  7. package/dist/esm/chunks/chunk-BMDYNMRC.js +1 -0
  8. package/dist/esm/chunks/chunk-CUL66KRA.js +1 -0
  9. package/dist/esm/chunks/{chunk-WT7RGGJU.js → chunk-CVNRODH3.js} +1 -1
  10. package/dist/esm/chunks/{chunk-N3V45JPS.js → chunk-CXHPBT7N.js} +1 -1
  11. package/dist/esm/chunks/chunk-GF6JTI65.js +1 -0
  12. package/dist/esm/chunks/chunk-MRSDJPBC.js +1 -0
  13. package/dist/esm/chunks/chunk-NYDZPTLP.js +1 -0
  14. package/dist/esm/chunks/chunk-QDHLXXAZ.js +1 -0
  15. package/dist/esm/chunks/{chunk-4HD4J5JC.js → chunk-QUECUXHD.js} +1 -1
  16. package/dist/esm/chunks/chunk-RJY76ESL.js +1 -0
  17. package/dist/esm/chunks/chunk-RPI5C3EJ.js +1 -0
  18. package/dist/esm/chunks/{chunk-6TFMLT4A.js → chunk-YC4JXWRG.js} +1 -1
  19. package/dist/esm/chunks/{diagnose-XCEBQKBO.js → diagnose-3XHPZZNP.js} +1 -1
  20. package/dist/esm/chunks/extras-C724KKG6.js +1 -0
  21. package/dist/esm/chunks/{markets-ui-M4IQ62CT.js → markets-ui-WBWWQAGU.js} +1 -1
  22. package/dist/esm/chunks/search-5NFIZBPJ.js +1 -0
  23. package/dist/esm/index.js +1 -1
  24. package/dist/esm/server.js +2 -2
  25. package/dist/tether.diagnose.js +2 -2
  26. package/dist/tether.diagnose.min.js +2 -2
  27. package/dist/tether.extras.js +33 -22
  28. package/dist/tether.extras.min.js +1 -1
  29. package/dist/tether.js +201 -186
  30. package/dist/tether.markets.js +13 -2
  31. package/dist/tether.markets.min.js +1 -1
  32. package/dist/tether.min.js +3 -3
  33. package/dist/tether.min.js.map +4 -4
  34. package/dist/types/src/cart/mutations.d.ts +2 -2
  35. package/dist/types/src/cart.d.ts +1 -0
  36. package/dist/types/src/client.d.ts +16 -11
  37. package/dist/types/src/index.d.ts +1 -1
  38. package/dist/types/src/markets.d.ts +9 -2
  39. package/dist/types/src/uri.d.ts +6 -0
  40. package/package.json +54 -5
  41. package/dist/esm/chunks/analytics-PG5FPRDN.js +0 -1
  42. package/dist/esm/chunks/chunk-E5YO6274.js +0 -1
  43. package/dist/esm/chunks/chunk-EW5SYO6L.js +0 -1
  44. package/dist/esm/chunks/chunk-HJY2JSNB.js +0 -1
  45. package/dist/esm/chunks/chunk-MQYB6BH4.js +0 -1
  46. package/dist/esm/chunks/chunk-RPMBCAGS.js +0 -1
  47. package/dist/esm/chunks/chunk-VFNHIGCS.js +0 -1
  48. package/dist/esm/chunks/chunk-YN6LEBEN.js +0 -1
  49. package/dist/esm/chunks/chunk-Z6HS3H7K.js +0 -1
  50. package/dist/esm/chunks/extras-4HU3VSBQ.js +0 -1
  51. package/dist/esm/chunks/search-A5DS2IOS.js +0 -1
package/dist/tether.js CHANGED
@@ -525,17 +525,28 @@
525
525
  function invalidResponse(res, conf) {
526
526
  return res.ok ? new TetherError(`Invalid response (${res.status})`, { code: "INVALID_RESPONSE", status: res.status }) : new TetherError(statusText(res.status, conf), { status: res.status });
527
527
  }
528
- function graphQLFailure(res, errors, conf) {
528
+ function graphQLFailure(res, errors, conf, code) {
529
529
  const message = errors.map((error) => error.message).filter(Boolean).join(" | ");
530
530
  let text2 = message || "Storefront API error";
531
531
  if (!res.ok) text2 = res.status === 401 || res.status === 403 || !message ? statusText(res.status, conf) : message;
532
- return new TetherError(text2, { status: res.status, errors });
532
+ return new TetherError(text2, { status: res.status, errors, code });
533
533
  }
534
534
  function networkFailure(error, retry) {
535
535
  if (retry === "any") return { kind: "retry", backoffMs: RETRY_BACKOFF_MS };
536
536
  throw new TetherError(`Network error: ${messageOf(error)}`, { code: "NETWORK" });
537
537
  }
538
- async function attempt(conf, query2, variables, isData, retry) {
538
+ function graphQLOutcome(res, conf, query2, errors, data, retry, operation) {
539
+ if (query2.includes(INVENTORY_FIELD) && errors.some((error) => error.message.includes(INVENTORY_FIELD))) {
540
+ return { kind: "inventory-refused", error: graphQLFailure(res, errors, conf) };
541
+ }
542
+ const codes = errors.map((error) => error.extensions?.code);
543
+ if (retry !== "none" && codes.includes("THROTTLED")) return { kind: "retry", backoffMs: THROTTLE_BACKOFF_MS };
544
+ const serverError = codes.includes("INTERNAL_SERVER_ERROR");
545
+ if (retry === "any" && serverError) return { kind: "retry", backoffMs: RETRY_BACKOFF_MS };
546
+ const unclear = res.ok && (serverError || operation === "mutation" && data == null);
547
+ throw graphQLFailure(res, errors, conf, unclear ? "SERVER_ERROR" : void 0);
548
+ }
549
+ async function attempt(conf, query2, variables, isData, retry, operation) {
539
550
  let res;
540
551
  try {
541
552
  res = await (conf.fetch ?? fetch)(endpoint(conf), {
@@ -562,15 +573,7 @@
562
573
  const body = toGraphQLResponse(json);
563
574
  if (!body) throw invalidResponse(res, conf);
564
575
  const { data, errors = [] } = body;
565
- if (errors.length) {
566
- if (query2.includes(INVENTORY_FIELD) && errors.some((error) => error.message.includes(INVENTORY_FIELD))) {
567
- return { kind: "inventory-refused", error: graphQLFailure(res, errors, conf) };
568
- }
569
- if (retry !== "none" && errors.some((error) => error.extensions?.code === "THROTTLED")) {
570
- return { kind: "retry", backoffMs: THROTTLE_BACKOFF_MS };
571
- }
572
- throw graphQLFailure(res, errors, conf);
573
- }
576
+ if (errors.length) return graphQLOutcome(res, conf, query2, errors, data, retry, operation);
574
577
  if (!res.ok) throw new TetherError(statusText(res.status, conf), { status: res.status });
575
578
  if (!isData(data)) throw invalidResponse(res, conf);
576
579
  return { kind: "data", data };
@@ -583,14 +586,15 @@
583
586
  const retryWhileLeft = operation === "mutation" ? "not-run" : "any";
584
587
  let retries = 0;
585
588
  for (; ; ) {
586
- const sent = queryText(conf, query2, { inventory: !storesWithoutInventory.has(store) }, { context, operation });
589
+ const access = { inventory: !storesWithoutInventory.has(store) };
590
+ const sent = queryText(conf, query2, access, { context, operation });
587
591
  const retry = retries < conf.retries ? retryWhileLeft : "none";
588
- const outcome = await attempt(conf, sent, variables, isData, retry);
592
+ const outcome = await attempt(conf, sent, variables, isData, retry, operation);
589
593
  switch (outcome.kind) {
590
594
  case "data":
591
595
  return outcome.data;
592
596
  case "inventory-refused":
593
- if (typeof query2 === "string") throw outcome.error;
597
+ if (typeof query2 === "string" || !access.inventory) throw outcome.error;
594
598
  log(`the token can't read inventory: ${INVENTORY_FIELD} is left out from now on`);
595
599
  storesWithoutInventory.add(store);
596
600
  break;
@@ -606,9 +610,11 @@
606
610
  return request(cfg, query2, variables, isData, options);
607
611
  }
608
612
  var hasData = (data) => typeof data === "object" && data !== null;
609
- function query(text2, variables = {}, options) {
610
- const operation = operationOf(typeof text2 === "string" ? text2 : text2({ inventory: true }));
611
- return gql(text2, variables, hasData, { ...options, operation });
613
+ function untypedQuery(conf, text2, variables = {}, options) {
614
+ return request(conf, text2, variables, hasData, { ...options, operation: operationOf(text2) });
615
+ }
616
+ function query(text2, variables, options) {
617
+ return untypedQuery(cfg, text2, variables, options);
612
618
  }
613
619
  function unwrap(payload, name) {
614
620
  if (!payload) throw new TetherError(`${name}: empty response`);
@@ -1680,6 +1686,160 @@
1680
1686
  return list;
1681
1687
  }
1682
1688
 
1689
+ // src/product-render.ts
1690
+ var NTH_IMAGE_BIND = /^image:(\d+)$/;
1691
+ var METAFIELD_BIND = "metafield:";
1692
+ var MULTI_LINE_METAFIELD = /^(rich_text|multi_line_text)_field$/;
1693
+ var ALL_PLAN_GROUPS = ["", "true", "all", "*"];
1694
+ var isSelect = (el) => el.tagName === "SELECT";
1695
+ var selectsIn = (scope, selector) => scope.$(`select${selector}`).filter(isSelect);
1696
+ function renderGallery(el, product) {
1697
+ const images = product.images.nodes;
1698
+ const rendered = [...el.children].map((node) => node.tagName === "IMG" ? node : node.querySelector("img"));
1699
+ const same = !hasTemplate(el) && rendered.length === images.length && rendered.every((img, index) => sameImage(img?.getAttribute("src"), images[index]?.url));
1700
+ if (same) return;
1701
+ repeat(el, images, (node, image) => {
1702
+ const target = node.matches(`img,${sel("bind", "image")}`) ? node : qsa(node, "img")[0] ?? node;
1703
+ setImage(target, image.url, image.altText || product.title);
1704
+ });
1705
+ }
1706
+ function renderMetafield(el, product, ref) {
1707
+ const dot = ref.indexOf(".");
1708
+ const namespace = ref.slice(0, dot);
1709
+ const key = ref.slice(dot + 1);
1710
+ const metafield = (product.metafields ?? []).find((field2) => field2?.namespace === namespace && field2.key === key);
1711
+ if (!metafield) {
1712
+ setText(el, "");
1713
+ return;
1714
+ }
1715
+ const image = metafield.reference?.image;
1716
+ if (image) {
1717
+ setImage(el, image.url, image.altText);
1718
+ return;
1719
+ }
1720
+ if (MULTI_LINE_METAFIELD.test(metafield.type)) el.style.whiteSpace = "pre-line";
1721
+ setText(el, metafield.value);
1722
+ }
1723
+ var PRODUCT_TEXTS = new Map(
1724
+ Object.entries({
1725
+ title: (product) => product.title,
1726
+ "description-text": (product) => product.description,
1727
+ vendor: (product) => product.vendor,
1728
+ type: (product) => product.productType,
1729
+ handle: (product) => product.handle,
1730
+ "published-at": (product) => formatDate(product.publishedAt),
1731
+ "min-price": (product) => formatMoney2(product.priceRange.minVariantPrice),
1732
+ "max-price": (product) => formatMoney2(product.priceRange.maxVariantPrice)
1733
+ })
1734
+ );
1735
+ function renderTags(el, product) {
1736
+ const exclude = (el.getAttribute("exclude") ?? "").split(",").map((tag) => tag.trim()).filter(Boolean);
1737
+ const tags = product.tags.filter((tag) => !exclude.includes(tag));
1738
+ repeat(el, tags, (node, tag) => {
1739
+ setText(node, tag);
1740
+ });
1741
+ }
1742
+ function renderProductBind(el, key, product) {
1743
+ const text2 = PRODUCT_TEXTS.get(key);
1744
+ if (text2) {
1745
+ setText(el, text2(product));
1746
+ return;
1747
+ }
1748
+ switch (key) {
1749
+ case "description":
1750
+ setStoreHtml(el, product.descriptionHtml);
1751
+ break;
1752
+ case "featured-image":
1753
+ setImage(el, product.featuredImage?.url, product.featuredImage?.altText || product.title);
1754
+ break;
1755
+ case "url":
1756
+ case "link": {
1757
+ const url2 = productUrl2(product.handle, product.id);
1758
+ if (url2) el.setAttribute("href", url2);
1759
+ if (key === "url" && !el.firstElementChild && !el.textContent.trim()) setText(el, product.title);
1760
+ break;
1761
+ }
1762
+ case "images":
1763
+ renderGallery(el, product);
1764
+ break;
1765
+ case "tags":
1766
+ renderTags(el, product);
1767
+ break;
1768
+ default:
1769
+ renderIndexedBind(el, key, product);
1770
+ }
1771
+ }
1772
+ function renderIndexedBind(el, key, product) {
1773
+ const nth = NTH_IMAGE_BIND.exec(key);
1774
+ if (nth) {
1775
+ const image = product.images.nodes[Number(nth[1]) - 1] ?? product.featuredImage;
1776
+ setImage(el, image?.url, image?.altText || product.title);
1777
+ } else if (key.startsWith(METAFIELD_BIND)) {
1778
+ renderMetafield(el, product, key.slice(METAFIELD_BIND.length));
1779
+ }
1780
+ }
1781
+ function renderOptionLists(scope) {
1782
+ const { product } = scope;
1783
+ scope.$(sel("option-list")).forEach((container) => {
1784
+ const name = scope.optName(get(container, "option-list"));
1785
+ const option = product.options.find((candidate) => candidate.name === name);
1786
+ if (!option || option.optionValues.length <= 1 && has(container, "hide-single")) {
1787
+ show(container, false);
1788
+ return;
1789
+ }
1790
+ repeat(container, option.optionValues, (node, value) => {
1791
+ node.setAttribute(attr("option-value"), value.name);
1792
+ const text2 = qsa(node, sel("bind", "option-value"))[0];
1793
+ if (text2) setText(text2, value.name);
1794
+ else if (!node.firstElementChild) setText(node, value.name);
1795
+ const swatchEl = qsa(node, sel("bind", "swatch"))[0];
1796
+ const { swatch } = value;
1797
+ if (!swatchEl || !swatch) return;
1798
+ if (swatch.color) swatchEl.style.backgroundColor = swatch.color;
1799
+ if (swatch.image?.previewImage) setImage(swatchEl, swatch.image.previewImage.url, value.name);
1800
+ });
1801
+ });
1802
+ }
1803
+ function renderSelects(scope) {
1804
+ const { product } = scope;
1805
+ selectsIn(scope, sel("option")).forEach((select) => {
1806
+ const option = product.options.find((candidate) => candidate.name === scope.optName(get(select, "option")));
1807
+ if (!option) {
1808
+ show(select, false);
1809
+ return;
1810
+ }
1811
+ select.replaceChildren(...option.optionValues.map((value) => new Option(value.name, value.name)));
1812
+ });
1813
+ selectsIn(scope, sel("variant-select")).forEach((select) => {
1814
+ select.replaceChildren(...product.variants.nodes.map((variant) => new Option(variant.title, variant.id)));
1815
+ });
1816
+ selectsIn(scope, sel("plan")).forEach((select) => {
1817
+ const plans = scope.plans.map((plan) => new Option(plan.name, plan.id));
1818
+ const oneTime = !product.requiresSellingPlan && get(select, "plan") !== "subscription-only";
1819
+ const oneTimeLabel = select.getAttribute(attr("one-time-label")) || "One-time purchase";
1820
+ select.replaceChildren(...oneTime ? [new Option(oneTimeLabel, "")] : [], ...plans);
1821
+ });
1822
+ }
1823
+ function renderPlanLists(scope) {
1824
+ scope.$(sel("plan-list")).forEach((container) => {
1825
+ const raw = (get(container, "plan-list") ?? "").trim();
1826
+ const group = ALL_PLAN_GROUPS.includes(raw.toLowerCase()) ? null : raw;
1827
+ const plans = scope.plans.filter((plan) => !group || plan.group.name === group);
1828
+ repeat(container, plans, (node, plan) => {
1829
+ node.setAttribute(attr("plan-value"), plan.id);
1830
+ if (!node.firstElementChild && !node.querySelector(sel("plan-bind"))) setText(node, plan.name);
1831
+ });
1832
+ });
1833
+ }
1834
+ function writeProduct(scope) {
1835
+ scope.$(sel("bind")).forEach((el) => {
1836
+ renderProductBind(el, get(el, "bind") ?? "", scope.product);
1837
+ });
1838
+ renderOptionLists(scope);
1839
+ renderSelects(scope);
1840
+ renderPlanLists(scope);
1841
+ }
1842
+
1683
1843
  // src/cart/render.ts
1684
1844
  var DEFAULT_TITLE = "Default Title";
1685
1845
  var quantityOf = (line, pending2) => pending2.get(line.id)?.quantity ?? line.quantity;
@@ -1781,7 +1941,8 @@
1781
1941
  if (value !== void 0) setTextTracked(el, value);
1782
1942
  });
1783
1943
  renderLineState(node, line, view);
1784
- qsa(node, `select${sel("line-plan")}`).forEach((select) => {
1944
+ const planSelects = qsa(node, sel("line-plan")).filter(isSelect);
1945
+ planSelects.forEach((select) => {
1785
1946
  getProduct(variant.product.handle).then((product) => {
1786
1947
  fillPlanSelect(select, line, product);
1787
1948
  }).catch((error) => {
@@ -2016,7 +2177,9 @@
2016
2177
  return (error.errors ?? []).some((graphQLError) => UNKNOWN_CART_MESSAGE.test(graphQLError.message));
2017
2178
  }
2018
2179
  function isUnclearFailure(error) {
2019
- return error instanceof TetherError && (error.code === "NETWORK" || error.code === "INVALID_RESPONSE" || (error.status ?? 0) >= 500);
2180
+ if (!(error instanceof TetherError)) return false;
2181
+ const { code, status: status2 = 0 } = error;
2182
+ return code === "NETWORK" || code === "INVALID_RESPONSE" || code === "SERVER_ERROR" || status2 >= 500;
2020
2183
  }
2021
2184
  function toLineInput(input, quantity = 1, options = {}) {
2022
2185
  const spec = typeof input === "object" && input !== null ? input : { ...options, quantity, ...input === null || input === void 0 ? {} : { variantId: input } };
@@ -2329,10 +2492,13 @@
2329
2492
  const ids = Array.isArray(lineIds) ? lineIds : [lineIds];
2330
2493
  return runChange(async () => commit(await removeLines(ids), "remove"));
2331
2494
  },
2495
+ /** Removes the lines the cart has when the change runs, so one queued just before it is cleared too. */
2332
2496
  clear() {
2333
- const ids = currentLines().map((line) => line.id);
2334
- if (!ids.length) return Promise.resolve(currentCart());
2335
- return Cart.removeLine(ids);
2497
+ return runChange(async () => {
2498
+ const ids = currentLines().map((line) => line.id);
2499
+ if (!ids.length) return currentCart();
2500
+ return commit(await removeLines(ids), "remove");
2501
+ });
2336
2502
  },
2337
2503
  setNote(note) {
2338
2504
  const variables = { note: note ?? "" };
@@ -2582,170 +2748,18 @@
2582
2748
  return new Map(Object.entries(texts));
2583
2749
  }
2584
2750
 
2585
- // src/product-render.ts
2586
- var NTH_IMAGE_BIND = /^image:(\d+)$/;
2587
- var METAFIELD_BIND = "metafield:";
2588
- var MULTI_LINE_METAFIELD = /^(rich_text|multi_line_text)_field$/;
2589
- var ALL_PLAN_GROUPS = ["", "true", "all", "*"];
2590
- var isSelect = (el) => el.tagName === "SELECT";
2591
- var selectsIn = (scope, selector) => scope.$(`select${selector}`).filter(isSelect);
2592
- function renderGallery(el, product) {
2593
- const images = product.images.nodes;
2594
- const rendered = [...el.children].map((node) => node.tagName === "IMG" ? node : node.querySelector("img"));
2595
- const same = !hasTemplate(el) && rendered.length === images.length && rendered.every((img, index) => sameImage(img?.getAttribute("src"), images[index]?.url));
2596
- if (same) return;
2597
- repeat(el, images, (node, image) => {
2598
- const target = node.matches(`img,${sel("bind", "image")}`) ? node : qsa(node, "img")[0] ?? node;
2599
- setImage(target, image.url, image.altText || product.title);
2600
- });
2601
- }
2602
- function renderMetafield(el, product, ref) {
2603
- const dot = ref.indexOf(".");
2604
- const namespace = ref.slice(0, dot);
2605
- const key = ref.slice(dot + 1);
2606
- const metafield = (product.metafields ?? []).find((field2) => field2?.namespace === namespace && field2.key === key);
2607
- if (!metafield) {
2608
- setText(el, "");
2609
- return;
2610
- }
2611
- const image = metafield.reference?.image;
2612
- if (image) {
2613
- setImage(el, image.url, image.altText);
2614
- return;
2615
- }
2616
- if (MULTI_LINE_METAFIELD.test(metafield.type)) el.style.whiteSpace = "pre-line";
2617
- setText(el, metafield.value);
2618
- }
2619
- var PRODUCT_TEXTS = new Map(
2620
- Object.entries({
2621
- title: (product) => product.title,
2622
- "description-text": (product) => product.description,
2623
- vendor: (product) => product.vendor,
2624
- type: (product) => product.productType,
2625
- handle: (product) => product.handle,
2626
- "published-at": (product) => formatDate(product.publishedAt),
2627
- "min-price": (product) => formatMoney2(product.priceRange.minVariantPrice),
2628
- "max-price": (product) => formatMoney2(product.priceRange.maxVariantPrice)
2629
- })
2630
- );
2631
- function renderTags(el, product) {
2632
- const exclude = (el.getAttribute("exclude") ?? "").split(",").map((tag) => tag.trim()).filter(Boolean);
2633
- const tags = product.tags.filter((tag) => !exclude.includes(tag));
2634
- repeat(el, tags, (node, tag) => {
2635
- setText(node, tag);
2636
- });
2637
- }
2638
- function renderProductBind(el, key, product) {
2639
- const text2 = PRODUCT_TEXTS.get(key);
2640
- if (text2) {
2641
- setText(el, text2(product));
2642
- return;
2643
- }
2644
- switch (key) {
2645
- case "description":
2646
- setStoreHtml(el, product.descriptionHtml);
2647
- break;
2648
- case "featured-image":
2649
- setImage(el, product.featuredImage?.url, product.featuredImage?.altText || product.title);
2650
- break;
2651
- case "url":
2652
- case "link": {
2653
- const url2 = productUrl2(product.handle, product.id);
2654
- if (url2) el.setAttribute("href", url2);
2655
- if (key === "url" && !el.firstElementChild && !el.textContent.trim()) setText(el, product.title);
2656
- break;
2657
- }
2658
- case "images":
2659
- renderGallery(el, product);
2660
- break;
2661
- case "tags":
2662
- renderTags(el, product);
2663
- break;
2664
- default:
2665
- renderIndexedBind(el, key, product);
2666
- }
2667
- }
2668
- function renderIndexedBind(el, key, product) {
2669
- const nth = NTH_IMAGE_BIND.exec(key);
2670
- if (nth) {
2671
- const image = product.images.nodes[Number(nth[1]) - 1] ?? product.featuredImage;
2672
- setImage(el, image?.url, image?.altText || product.title);
2673
- } else if (key.startsWith(METAFIELD_BIND)) {
2674
- renderMetafield(el, product, key.slice(METAFIELD_BIND.length));
2675
- }
2676
- }
2677
- function renderOptionLists(scope) {
2678
- const { product } = scope;
2679
- scope.$(sel("option-list")).forEach((container) => {
2680
- const name = scope.optName(get(container, "option-list"));
2681
- const option = product.options.find((candidate) => candidate.name === name);
2682
- if (!option || option.optionValues.length <= 1 && has(container, "hide-single")) {
2683
- show(container, false);
2684
- return;
2685
- }
2686
- repeat(container, option.optionValues, (node, value) => {
2687
- node.setAttribute(attr("option-value"), value.name);
2688
- const text2 = qsa(node, sel("bind", "option-value"))[0];
2689
- if (text2) setText(text2, value.name);
2690
- else if (!node.firstElementChild) setText(node, value.name);
2691
- const swatchEl = qsa(node, sel("bind", "swatch"))[0];
2692
- const { swatch } = value;
2693
- if (!swatchEl || !swatch) return;
2694
- if (swatch.color) swatchEl.style.backgroundColor = swatch.color;
2695
- if (swatch.image?.previewImage) setImage(swatchEl, swatch.image.previewImage.url, value.name);
2696
- });
2697
- });
2698
- }
2699
- function renderSelects(scope) {
2700
- const { product } = scope;
2701
- selectsIn(scope, sel("option")).forEach((select) => {
2702
- const option = product.options.find((candidate) => candidate.name === scope.optName(get(select, "option")));
2703
- if (!option) {
2704
- show(select, false);
2705
- return;
2706
- }
2707
- select.replaceChildren(...option.optionValues.map((value) => new Option(value.name, value.name)));
2708
- });
2709
- selectsIn(scope, sel("variant-select")).forEach((select) => {
2710
- select.replaceChildren(...product.variants.nodes.map((variant) => new Option(variant.title, variant.id)));
2711
- });
2712
- selectsIn(scope, sel("plan")).forEach((select) => {
2713
- const plans = scope.plans.map((plan) => new Option(plan.name, plan.id));
2714
- const oneTime = !product.requiresSellingPlan && get(select, "plan") !== "subscription-only";
2715
- const oneTimeLabel = select.getAttribute(attr("one-time-label")) || "One-time purchase";
2716
- select.replaceChildren(...oneTime ? [new Option(oneTimeLabel, "")] : [], ...plans);
2717
- });
2718
- }
2719
- function renderPlanLists(scope) {
2720
- scope.$(sel("plan-list")).forEach((container) => {
2721
- const raw = (get(container, "plan-list") ?? "").trim();
2722
- const group = ALL_PLAN_GROUPS.includes(raw.toLowerCase()) ? null : raw;
2723
- const plans = scope.plans.filter((plan) => !group || plan.group.name === group);
2724
- repeat(container, plans, (node, plan) => {
2725
- node.setAttribute(attr("plan-value"), plan.id);
2726
- if (!node.firstElementChild && !node.querySelector(sel("plan-bind"))) setText(node, plan.name);
2727
- });
2728
- });
2729
- }
2730
- function writeProduct(scope) {
2731
- scope.$(sel("bind")).forEach((el) => {
2732
- renderProductBind(el, get(el, "bind") ?? "", scope.product);
2733
- });
2734
- renderOptionLists(scope);
2735
- renderSelects(scope);
2736
- renderPlanLists(scope);
2737
- }
2738
-
2739
- // src/status.ts
2740
- var URL_SOURCE = "url:";
2741
- function decodePathSegment(segment) {
2751
+ // src/uri.ts
2752
+ function decodeOrKeep(text2) {
2742
2753
  try {
2743
- return decodeURIComponent(segment);
2754
+ return decodeURIComponent(text2);
2744
2755
  } catch (error) {
2745
- if (error instanceof URIError) return segment;
2756
+ if (error instanceof URIError) return text2;
2746
2757
  throw error;
2747
2758
  }
2748
2759
  }
2760
+
2761
+ // src/status.ts
2762
+ var URL_SOURCE = "url:";
2749
2763
  function resolveProductKey(el, location2 = window.location, name = "product") {
2750
2764
  const raw = (get(el, name) ?? "").trim();
2751
2765
  if (!raw.toLowerCase().startsWith(URL_SOURCE)) return { key: raw || null, fromUrl: false, raw };
@@ -2754,7 +2768,7 @@
2754
2768
  if (source.toLowerCase() === "path") {
2755
2769
  const segments = location2.pathname.split("/").filter(Boolean);
2756
2770
  const segment = segments[segments.length - 1];
2757
- key = segment === void 0 ? null : decodePathSegment(segment);
2771
+ key = segment === void 0 ? null : decodeOrKeep(segment);
2758
2772
  } else {
2759
2773
  key = new URLSearchParams(location2.search).get(source || "handle");
2760
2774
  }
@@ -3510,9 +3524,10 @@
3510
3524
  locale: typeof locale === "string" && locale ? locale : void 0
3511
3525
  };
3512
3526
  }
3527
+ var parseSavedMarket = (text2) => toMarket(parseJsonOrNull(text2));
3513
3528
  function readSavedMarket(storage = pageStorage) {
3514
3529
  const text2 = storage.getItem(storageKey());
3515
- return text2 ? toMarket(parseJsonOrNull(text2)) : null;
3530
+ return text2 ? parseSavedMarket(text2) : null;
3516
3531
  }
3517
3532
 
3518
3533
  // src/page-market.ts
@@ -3567,7 +3582,7 @@
3567
3582
  }
3568
3583
 
3569
3584
  // src/part-integrity.ts
3570
- var PART_INTEGRITY = { "tether.diagnose.js": "sha384-My35ltXsPvspuBHm0xCTu7wl6C4ORwigmKQMCaTKI6VP9ELMCCLYCETpAYW3Fus+", "tether.markets.js": "sha384-m9u+yVeYzqljJYufGt6C6E2xCgqgBSJstvmjU5a2lXZLK3q2saH19EYgP/EC5ox3", "tether.search.js": "sha384-ZrByfRsWNOHbbWOocY4WOD2RWe4QI2qQRZ4Eb+fM5v/UHIQ8mlWxbSwrxJt8yLbc", "tether.extras.js": "sha384-sE3c2fclN/3LKtuYUtC4CjVk76Zu0Mv/jR3p8YYLisAvyDvqJxpx4nj8PbJ3KzXH", "tether.account.js": "sha384-UF36poTg1vnXE7Pg+FYFGMJ4qjKhx9mbOug9bqTYhzgqRppOKS9M/1uY5D/cz9KF", "tether.account-pages.js": "sha384-jJYvZswrRw+pGDYrk0FswWcYqGpZkbR6ooxJGFu0lTYkUlVOcsCXuLLLU6M5kO54", "tether.analytics.js": "sha384-10v5pBMzgfF7BuLGQBg3cdbgMk701Uto5rGjhByiRD2jI9+/WF8DFcSWplIcdRNg" };
3585
+ var PART_INTEGRITY = { "tether.diagnose.js": "sha384-exWttcEw4tQTiJ4OU0r/Y+0T5vVIYIo3kcmieqDPkUUmJQGr2HZzWxZCOmMhsvaY", "tether.markets.js": "sha384-hn9741hCQun49mIj7A2bazMG2wXxTpMDScOQK+8wMeY6KT1p+ovXcKDi3m6BK04C", "tether.search.js": "sha384-ZrByfRsWNOHbbWOocY4WOD2RWe4QI2qQRZ4Eb+fM5v/UHIQ8mlWxbSwrxJt8yLbc", "tether.extras.js": "sha384-lt7I8DOnCx8wwVHQM4ZT39+Jlv95WVED5eVDezUKchNXo4+j1oBXYtxzPFiPyxgm", "tether.account.js": "sha384-UF36poTg1vnXE7Pg+FYFGMJ4qjKhx9mbOug9bqTYhzgqRppOKS9M/1uY5D/cz9KF", "tether.account-pages.js": "sha384-jJYvZswrRw+pGDYrk0FswWcYqGpZkbR6ooxJGFu0lTYkUlVOcsCXuLLLU6M5kO54", "tether.analytics.js": "sha384-10v5pBMzgfF7BuLGQBg3cdbgMk701Uto5rGjhByiRD2jI9+/WF8DFcSWplIcdRNg" };
3571
3586
 
3572
3587
  // src/part-registry.ts
3573
3588
  function isTetherInternals(value) {
@@ -4001,7 +4016,7 @@
4001
4016
  }
4002
4017
 
4003
4018
  // src/index.ts
4004
- var VERSION = "0.7.1";
4019
+ var VERSION = "0.7.2";
4005
4020
  var OBSERVE_DEBOUNCE_MS = 50;
4006
4021
  function pageConfig() {
4007
4022
  const config = pageGlobal("TETHER_CONFIG");
@@ -61,6 +61,16 @@
61
61
  }
62
62
  }
63
63
 
64
+ // src/uri.ts
65
+ function decodeOrKeep(text) {
66
+ try {
67
+ return decodeURIComponent(text);
68
+ } catch (error) {
69
+ if (error instanceof URIError) return text;
70
+ throw error;
71
+ }
72
+ }
73
+
64
74
  // src/markets.ts
65
75
  var DEFAULT_COOKIE = "tether_country";
66
76
  var storageKey = () => `${cfg.prefix}:market:${cfg.domain}`;
@@ -93,14 +103,15 @@
93
103
  locale: typeof locale === "string" && locale ? locale : void 0
94
104
  };
95
105
  }
106
+ var parseSavedMarket = (text) => toMarket(parseJsonOrNull(text));
96
107
  function readSavedMarket(storage = pageStorage) {
97
108
  const text = storage.getItem(storageKey());
98
- return text ? toMarket(parseJsonOrNull(text)) : null;
109
+ return text ? parseSavedMarket(text) : null;
99
110
  }
100
111
  function readCookie(doc, name) {
101
112
  if (!doc || !name) return null;
102
113
  const cookie = doc.cookie.split(";").map((part) => part.trim()).find((part) => part.startsWith(`${name}=`));
103
- return cookie ? decodeURIComponent(cookie.slice(name.length + 1)) : null;
114
+ return cookie ? decodeOrKeep(cookie.slice(name.length + 1)) : null;
104
115
  }
105
116
  function lookupLocale(locales, key) {
106
117
  if (!locales || !key) return null;
@@ -1 +1 @@
1
- (()=>{var W=Object.defineProperty;var G=(e,t)=>{for(var n in t)W(e,n,{get:t[n],enumerable:!0})};var b={};G(b,{detectMarket:()=>j,getLocalization:()=>J,renderCountrySelects:()=>L,setCountry:()=>xe});var V=window.Tether.__core["config.ts"],[c,_e,Pe,M,S,C,Ae]=V;var Z=window.Tether.__core["client.ts"],[ve,x,Oe,T,E]=Z;var Q=window.Tether.__core["cart.ts"],[g]=Q;var X=window.Tether.__core["collection.ts"],[_]=X;var ee=window.Tether.__core["loader.ts"],[P,Ue]=ee;var te=window.Tether.__core["dom.ts"],[De,m,Fe,A,je,$e,N,qe,Je,Ye,Be,Ke,We,Ge,w,Ve,Ze,Qe]=te;var ne=window.Tether.__core["mounting.ts"],[v,O,I,z,R,k]=ne;var oe=window.Tether.__core["market-codes.ts"],[s,d]=oe;function U(e,t="localStorage"){try{return window[t].getItem(e)}catch{return null}}function H(e,t,n="localStorage"){try{window[n].setItem(e,t)}catch{}}var re="tether_country",D=()=>`${c.prefix}:market:${c.domain}`,F={getItem:e=>U(e)};function ae(){let e=typeof window<"u";return{document:typeof document<"u"?document:null,location:e?window.location:null,storage:F,global:e?window:{}}}function ce(e){try{return JSON.parse(e)}catch(t){if(t instanceof SyntaxError)return null;throw t}}function ie(e){if(typeof e!="object"||e===null)return null;let t=s(Reflect.get(e,"country"));if(!t)return null;let n=Reflect.get(e,"locale");return{country:t,language:d(Reflect.get(e,"language"))??void 0,locale:typeof n=="string"&&n?n:void 0}}function ue(e=F){let t=e.getItem(D());return t?ie(ce(t)):null}function le(e,t){if(!e||!t)return null;let n=e.cookie.split(";").map(a=>a.trim()).find(a=>a.startsWith(`${t}=`));return n?decodeURIComponent(n.slice(t.length+1)):null}function h(e,t){if(!e||!t)return null;let n=t.toLowerCase(),a=Object.keys(e).find(o=>o.toLowerCase()===n);return a?e[a]??null:null}function se(e,t){let n=r=>t.document?.querySelector(`meta[name="${r}"]`)?.getAttribute("content"),a=Reflect.get(t.global,"TETHER_COUNTRY"),o=Reflect.get(t.global,"TETHER_LANGUAGE"),i=n("tether-country")||le(t.document,e.cookie||re);return{country:a||i,language:o||n("tether-language")}}function de(e,t){let n=(t.location?.pathname??"/").split("/").find(u=>u!==""),a=h(e.locales,n);if(a)return a;let o=t.document?.documentElement.getAttribute("lang");if(!o)return null;let i=h(e.locales,o)??h(e.locales,o.split("-")[0]);if(i)return{locale:o,...i};if(e.locales&&e.strict)return null;let r=o.split("-")[1];return{language:o,country:r?.length===2?r:null,locale:o}}function ge(e,t,n){switch(e){case"saved":return t.remember===!1?null:ue(n.storage);case"server":return se(t,n);case"webflow":return de(t,n)}}function fe(e){return e?e===!0?M:e:["saved"]}function j(e=c.markets,t=ae()){let n=(e===c.markets?e:S(e))??{};if(n.detect===!1)return{};let a={},o={sources:a};for(let i of fe(n.detect)){let r=ge(i,n,t);if(!r)continue;let u=s(r.country),l=d(r.language),f=typeof r.locale=="string"&&r.locale?r.locale:null;u&&!o.country&&(o.country=u,a.country=i),l&&!o.language&&(o.language=l,a.language=i),f&&!o.locale&&(o.locale=f,a.locale=i)}return o}function $(e){if(c.markets?.remember===!1)return;let t={country:e.country,language:e.language||void 0,locale:e.locale||void 0};H(D(),JSON.stringify(t))}var me="{name} ({currency})",pe="name",p=null;function ye(){p=null}var Ce="query TetherLocalization{localization{country{isoCode name currency{isoCode symbol}}language{isoCode name}availableCountries{isoCode name currency{isoCode symbol}availableLanguages{isoCode name}}}}",q={isoCode:"string",name:"string",currency:"object"},we=T({localization:{country:q,availableCountries:[q]}});function J(){return p??(p=x(Ce,{},we).then(e=>e.localization,e=>{throw p=null,e})),p}function Y(e,t){switch(t){case"name":return e.name;case"code":return e.isoCode;case"currency":return e.currency.isoCode;case"symbol":return e.currency.symbol;default:return""}}var ke=(e,t)=>e.replace(/\{(name|code|currency|symbol)\}/g,(n,a)=>Y(t,a));function he(e,t,n){let a=e.getAttribute(m("country-label"))||me,o=(e.getAttribute(m("countries"))||"").toUpperCase().split(/[\s,]+/).filter(Boolean),i=o.length?o.map(r=>t.find(u=>u.isoCode===r)).filter(r=>r!==void 0):t;e.replaceChildren(...i.map(r=>new Option(ke(a,r),r.isoCode))),e.value=n,e.value!==n&&(e.selectedIndex=-1),e.disabled=i.length<2&&!e.hasAttribute(m("always-enabled"))}function Le(e,t,n){let a=(c.country||n.country.isoCode).toUpperCase(),o=[...n.availableCountries].sort((r,u)=>r.name.localeCompare(u.name,c.locale||void 0)),i=o.find(r=>r.isoCode===a)??n.country;e.forEach(r=>{he(r,o,a)}),t.forEach(r=>{r.textContent=Y(i,N(r,"country-bind")||pe)})}function L(e=document){let t=k(e,`select${w("country-select")}`),n=k(e,w("country-bind"));return!t.length&&!n.length?Promise.resolve():J().then(a=>{Le(t,n,a)}).catch(a=>{C(a,"country select")})}var be=e=>Object.values(c.markets?.locales??{}).find(t=>s(t.country)===e);function Me(e,t){return e!==void 0?d(e):t?t.language?d(t.language):void 0:c.language}function Se(e){P();let t=[];return g.id()&&t.push(g.setBuyerIdentity({countryCode:e},{create:!1}).catch(n=>{C(n,"cart country")})),R(),v.forEach(n=>t.push(n.load())),O.forEach(n=>{n.closest(_())||(I.delete(n),z(n))}),t}async function xe(e,{language:t,locale:n,remember:a=!0}={}){let o=s(e);if(!o)throw new E(`Not a country code: ${e}`,{code:"INVALID_COUNTRY"});let i=be(o),r=n||i?.locale,u=r||c.locale,l=Me(t,i),f=o!==c.country||l!==c.language||u!==c.locale;if(c.country=o,c.language=l||void 0,c.locale=u,a&&$({country:o,language:l,locale:r}),!f)return L(),g.state;t!==void 0&&ye();let K=Se(o);return L(),A("country-changed",{country:o,language:c.language}),await Promise.all(K),g.state}function y(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function Te(e){return y(e)&&y(e.__core)&&y(e.__parts)}function B(e,t){let n=Reflect.get(window,"Tether");if(!Te(n))throw new Error(`tether.${e}.js runs after the Tether script, which loads it, and needs its window.Tether`);n.__parts[e]=t}B("markets",b);})();
1
+ (()=>{var G=Object.defineProperty;var V=(e,t)=>{for(var n in t)G(e,n,{get:t[n],enumerable:!0})};var b={};V(b,{detectMarket:()=>$,getLocalization:()=>J,renderCountrySelects:()=>L,setCountry:()=>Ee});var Z=window.Tether.__core["config.ts"],[c,Ae,Ne,M,S,C,ve]=Z;var Q=window.Tether.__core["client.ts"],[Ie,x,Re,T,E]=Q;var X=window.Tether.__core["cart.ts"],[g]=X;var ee=window.Tether.__core["collection.ts"],[_]=ee;var te=window.Tether.__core["loader.ts"],[P,De]=te;var ne=window.Tether.__core["dom.ts"],[je,m,$e,A,qe,Ke,N,Je,Ye,Be,We,Ge,Ve,Ze,w,Qe,Xe,et]=ne;var oe=window.Tether.__core["mounting.ts"],[v,O,I,R,z,k]=oe;var re=window.Tether.__core["market-codes.ts"],[s,d]=re;function U(e,t="localStorage"){try{return window[t].getItem(e)}catch{return null}}function H(e,t,n="localStorage"){try{window[n].setItem(e,t)}catch{}}function D(e){try{return decodeURIComponent(e)}catch(t){if(t instanceof URIError)return e;throw t}}var ae="tether_country",F=()=>`${c.prefix}:market:${c.domain}`,j={getItem:e=>U(e)};function ce(){let e=typeof window<"u";return{document:typeof document<"u"?document:null,location:e?window.location:null,storage:j,global:e?window:{}}}function ie(e){try{return JSON.parse(e)}catch(t){if(t instanceof SyntaxError)return null;throw t}}function ue(e){if(typeof e!="object"||e===null)return null;let t=s(Reflect.get(e,"country"));if(!t)return null;let n=Reflect.get(e,"locale");return{country:t,language:d(Reflect.get(e,"language"))??void 0,locale:typeof n=="string"&&n?n:void 0}}var le=e=>ue(ie(e));function se(e=j){let t=e.getItem(F());return t?le(t):null}function de(e,t){if(!e||!t)return null;let n=e.cookie.split(";").map(a=>a.trim()).find(a=>a.startsWith(`${t}=`));return n?D(n.slice(t.length+1)):null}function h(e,t){if(!e||!t)return null;let n=t.toLowerCase(),a=Object.keys(e).find(o=>o.toLowerCase()===n);return a?e[a]??null:null}function ge(e,t){let n=r=>t.document?.querySelector(`meta[name="${r}"]`)?.getAttribute("content"),a=Reflect.get(t.global,"TETHER_COUNTRY"),o=Reflect.get(t.global,"TETHER_LANGUAGE"),i=n("tether-country")||de(t.document,e.cookie||ae);return{country:a||i,language:o||n("tether-language")}}function fe(e,t){let n=(t.location?.pathname??"/").split("/").find(u=>u!==""),a=h(e.locales,n);if(a)return a;let o=t.document?.documentElement.getAttribute("lang");if(!o)return null;let i=h(e.locales,o)??h(e.locales,o.split("-")[0]);if(i)return{locale:o,...i};if(e.locales&&e.strict)return null;let r=o.split("-")[1];return{language:o,country:r?.length===2?r:null,locale:o}}function me(e,t,n){switch(e){case"saved":return t.remember===!1?null:se(n.storage);case"server":return ge(t,n);case"webflow":return fe(t,n)}}function pe(e){return e?e===!0?M:e:["saved"]}function $(e=c.markets,t=ce()){let n=(e===c.markets?e:S(e))??{};if(n.detect===!1)return{};let a={},o={sources:a};for(let i of pe(n.detect)){let r=me(i,n,t);if(!r)continue;let u=s(r.country),l=d(r.language),f=typeof r.locale=="string"&&r.locale?r.locale:null;u&&!o.country&&(o.country=u,a.country=i),l&&!o.language&&(o.language=l,a.language=i),f&&!o.locale&&(o.locale=f,a.locale=i)}return o}function q(e){if(c.markets?.remember===!1)return;let t={country:e.country,language:e.language||void 0,locale:e.locale||void 0};H(F(),JSON.stringify(t))}var ye="{name} ({currency})",Ce="name",p=null;function we(){p=null}var ke="query TetherLocalization{localization{country{isoCode name currency{isoCode symbol}}language{isoCode name}availableCountries{isoCode name currency{isoCode symbol}availableLanguages{isoCode name}}}}",K={isoCode:"string",name:"string",currency:"object"},he=T({localization:{country:K,availableCountries:[K]}});function J(){return p??(p=x(ke,{},he).then(e=>e.localization,e=>{throw p=null,e})),p}function Y(e,t){switch(t){case"name":return e.name;case"code":return e.isoCode;case"currency":return e.currency.isoCode;case"symbol":return e.currency.symbol;default:return""}}var Le=(e,t)=>e.replace(/\{(name|code|currency|symbol)\}/g,(n,a)=>Y(t,a));function be(e,t,n){let a=e.getAttribute(m("country-label"))||ye,o=(e.getAttribute(m("countries"))||"").toUpperCase().split(/[\s,]+/).filter(Boolean),i=o.length?o.map(r=>t.find(u=>u.isoCode===r)).filter(r=>r!==void 0):t;e.replaceChildren(...i.map(r=>new Option(Le(a,r),r.isoCode))),e.value=n,e.value!==n&&(e.selectedIndex=-1),e.disabled=i.length<2&&!e.hasAttribute(m("always-enabled"))}function Me(e,t,n){let a=(c.country||n.country.isoCode).toUpperCase(),o=[...n.availableCountries].sort((r,u)=>r.name.localeCompare(u.name,c.locale||void 0)),i=o.find(r=>r.isoCode===a)??n.country;e.forEach(r=>{be(r,o,a)}),t.forEach(r=>{r.textContent=Y(i,N(r,"country-bind")||Ce)})}function L(e=document){let t=k(e,`select${w("country-select")}`),n=k(e,w("country-bind"));return!t.length&&!n.length?Promise.resolve():J().then(a=>{Me(t,n,a)}).catch(a=>{C(a,"country select")})}var Se=e=>Object.values(c.markets?.locales??{}).find(t=>s(t.country)===e);function xe(e,t){return e!==void 0?d(e):t?t.language?d(t.language):void 0:c.language}function Te(e){P();let t=[];return g.id()&&t.push(g.setBuyerIdentity({countryCode:e},{create:!1}).catch(n=>{C(n,"cart country")})),z(),v.forEach(n=>t.push(n.load())),O.forEach(n=>{n.closest(_())||(I.delete(n),R(n))}),t}async function Ee(e,{language:t,locale:n,remember:a=!0}={}){let o=s(e);if(!o)throw new E(`Not a country code: ${e}`,{code:"INVALID_COUNTRY"});let i=Se(o),r=n||i?.locale,u=r||c.locale,l=xe(t,i),f=o!==c.country||l!==c.language||u!==c.locale;if(c.country=o,c.language=l||void 0,c.locale=u,a&&q({country:o,language:l,locale:r}),!f)return L(),g.state;t!==void 0&&we();let W=Te(o);return L(),A("country-changed",{country:o,language:c.language}),await Promise.all(W),g.state}function y(e){return e!==null&&typeof e=="object"&&!Array.isArray(e)}function _e(e){return y(e)&&y(e.__core)&&y(e.__parts)}function B(e,t){let n=Reflect.get(window,"Tether");if(!_e(n))throw new Error(`tether.${e}.js runs after the Tether script, which loads it, and needs its window.Tether`);n.__parts[e]=t}B("markets",b);})();