@occluze/tether 0.7.0 → 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.
- package/README.md +51 -109
- package/dist/esm/astro.js +1 -1
- package/dist/esm/chunks/{account-UIPWJQ3Y.js → account-W4FBRKXA.js} +1 -1
- package/dist/esm/chunks/{account-pages-6ROA6Y4V.js → account-pages-IM3OQTOP.js} +1 -1
- package/dist/esm/chunks/analytics-QDVOLZY3.js +1 -0
- package/dist/esm/chunks/{chunk-T44MUL4Y.js → chunk-AUVAN3KZ.js} +1 -1
- package/dist/esm/chunks/chunk-BMDYNMRC.js +1 -0
- package/dist/esm/chunks/chunk-CUL66KRA.js +1 -0
- package/dist/esm/chunks/{chunk-6OZJCAGZ.js → chunk-CVNRODH3.js} +1 -1
- package/dist/esm/chunks/{chunk-5Z5O3767.js → chunk-CXHPBT7N.js} +1 -1
- package/dist/esm/chunks/chunk-GF6JTI65.js +1 -0
- package/dist/esm/chunks/chunk-MRSDJPBC.js +1 -0
- package/dist/esm/chunks/chunk-NYDZPTLP.js +1 -0
- package/dist/esm/chunks/chunk-QDHLXXAZ.js +1 -0
- package/dist/esm/chunks/{chunk-4GCXM764.js → chunk-QUECUXHD.js} +1 -1
- package/dist/esm/chunks/chunk-RJY76ESL.js +1 -0
- package/dist/esm/chunks/chunk-RPI5C3EJ.js +1 -0
- package/dist/esm/chunks/{chunk-BFTZR4OB.js → chunk-YC4JXWRG.js} +2 -2
- package/dist/esm/chunks/{diagnose-3FEKRIEK.js → diagnose-3XHPZZNP.js} +1 -1
- package/dist/esm/chunks/extras-C724KKG6.js +1 -0
- package/dist/esm/chunks/{markets-ui-FD6NI2OG.js → markets-ui-WBWWQAGU.js} +1 -1
- package/dist/esm/chunks/{search-UJ73YBQ5.js → search-5NFIZBPJ.js} +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +2 -2
- package/dist/tether.account-pages.js +13 -3
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +13 -3
- package/dist/tether.account.min.js +1 -1
- package/dist/tether.analytics.js +18 -3
- package/dist/tether.analytics.min.js +1 -1
- package/dist/tether.diagnose.js +20 -5
- package/dist/tether.diagnose.min.js +2 -2
- package/dist/tether.extras.js +52 -28
- package/dist/tether.extras.min.js +1 -1
- package/dist/tether.js +353 -288
- package/dist/tether.markets.js +31 -5
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +2 -2
- package/dist/tether.min.js.map +4 -4
- package/dist/tether.search.js +18 -3
- package/dist/tether.search.min.js +1 -1
- package/dist/types/server/src/shared/cart-id.d.ts +2 -0
- package/dist/types/src/cart/mutations.d.ts +6 -0
- package/dist/types/src/cart/store.d.ts +5 -2
- package/dist/types/src/cart.d.ts +3 -2
- package/dist/types/src/client.d.ts +61 -22
- package/dist/types/src/config.d.ts +6 -1
- package/dist/types/src/index.d.ts +2 -2
- package/dist/types/src/markets.d.ts +9 -2
- package/dist/types/src/product-shape.d.ts +3 -1
- package/dist/types/src/uri.d.ts +6 -0
- package/package.json +54 -6
- package/dist/esm/chunks/analytics-2V632IRV.js +0 -1
- package/dist/esm/chunks/chunk-56FCLJJD.js +0 -1
- package/dist/esm/chunks/chunk-A5SYCVQM.js +0 -1
- package/dist/esm/chunks/chunk-BA4LQJJQ.js +0 -1
- package/dist/esm/chunks/chunk-C5JYQCWH.js +0 -1
- package/dist/esm/chunks/chunk-E5YO6274.js +0 -1
- package/dist/esm/chunks/chunk-MPDWM2NN.js +0 -1
- package/dist/esm/chunks/chunk-YLUULATY.js +0 -1
- package/dist/esm/chunks/extras-6POFBNOP.js +0 -1
- package/src/account/addresses-view.ts +0 -58
- package/src/account/answers.ts +0 -79
- package/src/account/markup.ts +0 -36
- package/src/account/operations.ts +0 -145
- package/src/account/order-lines.ts +0 -75
- package/src/account/order-list.ts +0 -107
- package/src/account/order-page.ts +0 -137
- package/src/account/page-answers.ts +0 -317
- package/src/account/page-events.ts +0 -253
- package/src/account/server-client.ts +0 -183
- package/src/account/subscriptions-view.ts +0 -115
- package/src/account-api.ts +0 -98
- package/src/account-marks.ts +0 -35
- package/src/account-pages.ts +0 -81
- package/src/account.ts +0 -284
- package/src/amounts.ts +0 -64
- package/src/analytics-shopify.ts +0 -246
- package/src/analytics.ts +0 -315
- package/src/api-version.ts +0 -79
- package/src/astro.ts +0 -272
- package/src/cart/cart-shape.ts +0 -25
- package/src/cart/checkout.ts +0 -101
- package/src/cart/drawer.ts +0 -125
- package/src/cart/mutations.ts +0 -237
- package/src/cart/render.ts +0 -266
- package/src/cart/store.ts +0 -279
- package/src/cart.ts +0 -345
- package/src/client.ts +0 -391
- package/src/collection.ts +0 -175
- package/src/config-sections.ts +0 -284
- package/src/config.ts +0 -480
- package/src/controls/cart-controls.ts +0 -123
- package/src/controls/discount.ts +0 -75
- package/src/controls/extras.ts +0 -54
- package/src/controls/navigation.ts +0 -77
- package/src/diagnose-markup.ts +0 -298
- package/src/diagnose.ts +0 -294
- package/src/dom.ts +0 -268
- package/src/extras.ts +0 -136
- package/src/fragments.ts +0 -261
- package/src/globals.d.ts +0 -15
- package/src/images.ts +0 -91
- package/src/index.ts +0 -415
- package/src/initial.ts +0 -101
- package/src/lazy-iife.ts +0 -197
- package/src/lazy.ts +0 -42
- package/src/loader.ts +0 -363
- package/src/market-codes.ts +0 -6
- package/src/markets-ui.ts +0 -227
- package/src/markets.ts +0 -246
- package/src/markup.ts +0 -127
- package/src/mounting.ts +0 -264
- package/src/page-market.ts +0 -110
- package/src/part-integrity.ts +0 -7
- package/src/parts/account-pages.ts +0 -7
- package/src/parts/account.ts +0 -7
- package/src/parts/analytics.ts +0 -7
- package/src/parts/diagnose.ts +0 -7
- package/src/parts/extras.ts +0 -7
- package/src/parts/markets.ts +0 -7
- package/src/parts/search.ts +0 -7
- package/src/presets/barba.ts +0 -194
- package/src/product-events.ts +0 -115
- package/src/product-render.ts +0 -218
- package/src/product-shape.ts +0 -49
- package/src/product-state.ts +0 -318
- package/src/product.ts +0 -308
- package/src/repeaters.ts +0 -161
- package/src/search.ts +0 -305
- package/src/selling-plans.ts +0 -145
- package/src/server.ts +0 -211
- package/src/shopify-values.ts +0 -91
- package/src/status.ts +0 -106
- package/src/storage.ts +0 -34
- package/src/store-html.ts +0 -359
- package/src/storefront/catalog.ts +0 -196
- package/src/storefront/json-ld.ts +0 -144
- package/src/variant-render.ts +0 -139
package/dist/tether.js
CHANGED
|
@@ -452,7 +452,7 @@
|
|
|
452
452
|
if (typeof shape === "function") return shape(value);
|
|
453
453
|
if (typeof shape === "string") return shape === "object" ? isRecord(value) : typeof value === shape;
|
|
454
454
|
if (isShapeList(shape)) return Array.isArray(value) && value.every((item) => matches(item, shape[0]));
|
|
455
|
-
return isRecord(value) && Object.entries(shape).every(([key, inner]) => matches(value[key], inner));
|
|
455
|
+
return isRecord(value) && Object.entries(shape).every(([key, inner]) => inner === void 0 || matches(value[key], inner));
|
|
456
456
|
}
|
|
457
457
|
var isShapeList = (shape) => Array.isArray(shape);
|
|
458
458
|
var orNull = (shape) => (value) => value === null || matches(value, shape);
|
|
@@ -462,6 +462,9 @@
|
|
|
462
462
|
}
|
|
463
463
|
var STRINGS_AND_COMMENTS = /"(?:\\.|[^"\\\n])*"|#[^\n]*/g;
|
|
464
464
|
var OPERATION_START = /^\s*(?:query|mutation)(?:\s+\w+)?\s*(?:\([^)]*\))?/;
|
|
465
|
+
function operationOf(document2) {
|
|
466
|
+
return /\bmutation\b/.test(document2.replace(STRINGS_AND_COMMENTS, "")) ? "mutation" : "query";
|
|
467
|
+
}
|
|
465
468
|
function inContext(query2, { country, language }) {
|
|
466
469
|
const args = [];
|
|
467
470
|
if (country) args.push(`country: ${country}`);
|
|
@@ -489,10 +492,10 @@
|
|
|
489
492
|
var INVENTORY_FIELD = "quantityAvailable";
|
|
490
493
|
var storesWithoutInventory = /* @__PURE__ */ new Set();
|
|
491
494
|
var storeOf = (conf) => `${conf.domain} ${conf.apiVersion} ${conf.token}`;
|
|
492
|
-
function queryText(conf, query2, access, context) {
|
|
495
|
+
function queryText(conf, query2, access, { context, operation }) {
|
|
493
496
|
const written = typeof query2 === "string" ? query2 : query2(access);
|
|
494
497
|
const text2 = context ? inContext(written, conf) : written;
|
|
495
|
-
if (conf.maxAge <= 0 ||
|
|
498
|
+
if (conf.maxAge <= 0 || operation === "mutation") return text2;
|
|
496
499
|
return `${text2} #${Math.floor(Date.now() / (conf.maxAge * MS_PER_SECOND))}`;
|
|
497
500
|
}
|
|
498
501
|
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -520,16 +523,30 @@
|
|
|
520
523
|
return { data, errors: errors.map(toGraphQLError) };
|
|
521
524
|
}
|
|
522
525
|
function invalidResponse(res, conf) {
|
|
523
|
-
|
|
524
|
-
return new TetherError(text2, { status: res.status });
|
|
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 });
|
|
525
527
|
}
|
|
526
|
-
function graphQLFailure(res, errors, conf) {
|
|
528
|
+
function graphQLFailure(res, errors, conf, code) {
|
|
527
529
|
const message = errors.map((error) => error.message).filter(Boolean).join(" | ");
|
|
528
530
|
let text2 = message || "Storefront API error";
|
|
529
531
|
if (!res.ok) text2 = res.status === 401 || res.status === 403 || !message ? statusText(res.status, conf) : message;
|
|
530
|
-
return new TetherError(text2, { status: res.status, errors });
|
|
532
|
+
return new TetherError(text2, { status: res.status, errors, code });
|
|
533
|
+
}
|
|
534
|
+
function networkFailure(error, retry) {
|
|
535
|
+
if (retry === "any") return { kind: "retry", backoffMs: RETRY_BACKOFF_MS };
|
|
536
|
+
throw new TetherError(`Network error: ${messageOf(error)}`, { code: "NETWORK" });
|
|
537
|
+
}
|
|
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);
|
|
531
548
|
}
|
|
532
|
-
async function attempt(conf, query2, variables, isData,
|
|
549
|
+
async function attempt(conf, query2, variables, isData, retry, operation) {
|
|
533
550
|
let res;
|
|
534
551
|
try {
|
|
535
552
|
res = await (conf.fetch ?? fetch)(endpoint(conf), {
|
|
@@ -538,53 +555,46 @@
|
|
|
538
555
|
body: JSON.stringify({ query: query2, variables })
|
|
539
556
|
});
|
|
540
557
|
} catch (error) {
|
|
541
|
-
|
|
542
|
-
throw new TetherError(`Network error: ${messageOf(error)}`, {
|
|
543
|
-
code: "NETWORK"
|
|
544
|
-
});
|
|
558
|
+
return networkFailure(error, retry);
|
|
545
559
|
}
|
|
546
560
|
if (res.status === SECURITY_REJECTION) {
|
|
547
561
|
throw new TetherError("Shopify security rejection (430)", { status: SECURITY_REJECTION });
|
|
548
562
|
}
|
|
549
|
-
if (
|
|
563
|
+
if (retry !== "none" && res.status === 429 || retry === "any" && res.status >= 500) {
|
|
550
564
|
return { kind: "retry", backoffMs: RETRY_BACKOFF_MS };
|
|
551
565
|
}
|
|
552
566
|
let json;
|
|
553
567
|
try {
|
|
554
568
|
json = await res.json();
|
|
555
|
-
} catch {
|
|
556
|
-
throw invalidResponse(res, conf);
|
|
569
|
+
} catch (error) {
|
|
570
|
+
if (error instanceof SyntaxError) throw invalidResponse(res, conf);
|
|
571
|
+
return networkFailure(error, retry);
|
|
557
572
|
}
|
|
558
573
|
const body = toGraphQLResponse(json);
|
|
559
574
|
if (!body) throw invalidResponse(res, conf);
|
|
560
575
|
const { data, errors = [] } = body;
|
|
561
|
-
if (errors.length)
|
|
562
|
-
if (query2.includes(INVENTORY_FIELD) && errors.some((error) => error.message.includes(INVENTORY_FIELD))) {
|
|
563
|
-
return { kind: "inventory-refused", error: graphQLFailure(res, errors, conf) };
|
|
564
|
-
}
|
|
565
|
-
if (canRetry && errors.some((error) => error.extensions?.code === "THROTTLED")) {
|
|
566
|
-
return { kind: "retry", backoffMs: THROTTLE_BACKOFF_MS };
|
|
567
|
-
}
|
|
568
|
-
throw graphQLFailure(res, errors, conf);
|
|
569
|
-
}
|
|
576
|
+
if (errors.length) return graphQLOutcome(res, conf, query2, errors, data, retry, operation);
|
|
570
577
|
if (!res.ok) throw new TetherError(statusText(res.status, conf), { status: res.status });
|
|
571
578
|
if (!isData(data)) throw invalidResponse(res, conf);
|
|
572
579
|
return { kind: "data", data };
|
|
573
580
|
}
|
|
574
|
-
async function request(conf, query2, variables, isData, { context = true } = {}) {
|
|
581
|
+
async function request(conf, query2, variables, isData, { context = true, operation = "query" } = {}) {
|
|
575
582
|
if (!conf.domain || !conf.token) {
|
|
576
583
|
throw new TetherError("Tether is not configured: domain and token are required", { code: "NOT_CONFIGURED" });
|
|
577
584
|
}
|
|
578
585
|
const store = storeOf(conf);
|
|
586
|
+
const retryWhileLeft = operation === "mutation" ? "not-run" : "any";
|
|
579
587
|
let retries = 0;
|
|
580
588
|
for (; ; ) {
|
|
581
|
-
const
|
|
582
|
-
const
|
|
589
|
+
const access = { inventory: !storesWithoutInventory.has(store) };
|
|
590
|
+
const sent = queryText(conf, query2, access, { context, operation });
|
|
591
|
+
const retry = retries < conf.retries ? retryWhileLeft : "none";
|
|
592
|
+
const outcome = await attempt(conf, sent, variables, isData, retry, operation);
|
|
583
593
|
switch (outcome.kind) {
|
|
584
594
|
case "data":
|
|
585
595
|
return outcome.data;
|
|
586
596
|
case "inventory-refused":
|
|
587
|
-
if (typeof query2 === "string") throw outcome.error;
|
|
597
|
+
if (typeof query2 === "string" || !access.inventory) throw outcome.error;
|
|
588
598
|
log(`the token can't read inventory: ${INVENTORY_FIELD} is left out from now on`);
|
|
589
599
|
storesWithoutInventory.add(store);
|
|
590
600
|
break;
|
|
@@ -600,8 +610,11 @@
|
|
|
600
610
|
return request(cfg, query2, variables, isData, options);
|
|
601
611
|
}
|
|
602
612
|
var hasData = (data) => typeof data === "object" && data !== null;
|
|
603
|
-
function
|
|
604
|
-
return
|
|
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);
|
|
605
618
|
}
|
|
606
619
|
function unwrap(payload, name) {
|
|
607
620
|
if (!payload) throw new TetherError(`${name}: empty response`);
|
|
@@ -1354,7 +1367,10 @@
|
|
|
1354
1367
|
priceRange: { minVariantPrice: MONEY2, maxVariantPrice: MONEY2 },
|
|
1355
1368
|
variants: { nodes: [{ id: "string", price: MONEY2, selectedOptions: ["object"] }] }
|
|
1356
1369
|
};
|
|
1357
|
-
var PRODUCT_CONNECTION = {
|
|
1370
|
+
var PRODUCT_CONNECTION = {
|
|
1371
|
+
nodes: [PRODUCT],
|
|
1372
|
+
pageInfo: { hasNextPage: "boolean" }
|
|
1373
|
+
};
|
|
1358
1374
|
var isProductShape = (value) => field(value, "id") !== "" && field(value, "handle") !== "" && matches(value, PRODUCT);
|
|
1359
1375
|
var PRODUCT_NODE = (node) => isRecord(node) && (node["id"] === void 0 || matches(node, PRODUCT));
|
|
1360
1376
|
|
|
@@ -1670,6 +1686,160 @@
|
|
|
1670
1686
|
return list;
|
|
1671
1687
|
}
|
|
1672
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
|
+
|
|
1673
1843
|
// src/cart/render.ts
|
|
1674
1844
|
var DEFAULT_TITLE = "Default Title";
|
|
1675
1845
|
var quantityOf = (line, pending2) => pending2.get(line.id)?.quantity ?? line.quantity;
|
|
@@ -1771,7 +1941,8 @@
|
|
|
1771
1941
|
if (value !== void 0) setTextTracked(el, value);
|
|
1772
1942
|
});
|
|
1773
1943
|
renderLineState(node, line, view);
|
|
1774
|
-
qsa(node,
|
|
1944
|
+
const planSelects = qsa(node, sel("line-plan")).filter(isSelect);
|
|
1945
|
+
planSelects.forEach((select) => {
|
|
1775
1946
|
getProduct(variant.product.handle).then((product) => {
|
|
1776
1947
|
fillPlanSelect(select, line, product);
|
|
1777
1948
|
}).catch((error) => {
|
|
@@ -1876,6 +2047,9 @@
|
|
|
1876
2047
|
}
|
|
1877
2048
|
}
|
|
1878
2049
|
|
|
2050
|
+
// server/src/shared/cart-id.ts
|
|
2051
|
+
var CART_ID = /^gid:\/\/shopify\/Cart\/[\w-]+(\?key=[\w-]+)?$/;
|
|
2052
|
+
|
|
1879
2053
|
// src/cart/store.ts
|
|
1880
2054
|
var status = { kind: "unknown" };
|
|
1881
2055
|
var warnings = [];
|
|
@@ -1894,7 +2068,13 @@
|
|
|
1894
2068
|
var currentWarnings = () => warnings;
|
|
1895
2069
|
var statusOf = (cart) => cart ? { kind: "ready", cart } : { kind: "empty" };
|
|
1896
2070
|
var seenKey = () => `${cfg.storageKey}:seen`;
|
|
1897
|
-
|
|
2071
|
+
function savedCartId() {
|
|
2072
|
+
const stored = getStored(cfg.storageKey);
|
|
2073
|
+
if (!stored) return null;
|
|
2074
|
+
if (CART_ID.test(stored)) return stored;
|
|
2075
|
+
forgetSavedCart();
|
|
2076
|
+
return null;
|
|
2077
|
+
}
|
|
1898
2078
|
function forgetSavedCart() {
|
|
1899
2079
|
removeStored(cfg.storageKey);
|
|
1900
2080
|
}
|
|
@@ -1996,6 +2176,11 @@
|
|
|
1996
2176
|
if (error.userErrors?.some((userError) => userError.field?.[0] === "cartId")) return true;
|
|
1997
2177
|
return (error.errors ?? []).some((graphQLError) => UNKNOWN_CART_MESSAGE.test(graphQLError.message));
|
|
1998
2178
|
}
|
|
2179
|
+
function isUnclearFailure(error) {
|
|
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;
|
|
2183
|
+
}
|
|
1999
2184
|
function toLineInput(input, quantity = 1, options = {}) {
|
|
2000
2185
|
const spec = typeof input === "object" && input !== null ? input : { ...options, quantity, ...input === null || input === void 0 ? {} : { variantId: input } };
|
|
2001
2186
|
const merchandiseId = GID.variant(spec.merchandiseId || spec.variantId || spec.id);
|
|
@@ -2012,7 +2197,7 @@
|
|
|
2012
2197
|
return (access) => `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}warnings{code message target}}}${cartFragment(access)}`;
|
|
2013
2198
|
}
|
|
2014
2199
|
async function send(name, args, inner, variables) {
|
|
2015
|
-
const data = await gql(mutationText(name, args, inner), variables, isPayloadOf(name));
|
|
2200
|
+
const data = await gql(mutationText(name, args, inner), variables, isPayloadOf(name), { operation: "mutation" });
|
|
2016
2201
|
const payload = unwrap(data[name], name);
|
|
2017
2202
|
setWarnings(payload.warnings ?? []);
|
|
2018
2203
|
return payload.cart;
|
|
@@ -2139,6 +2324,25 @@
|
|
|
2139
2324
|
|
|
2140
2325
|
// src/cart.ts
|
|
2141
2326
|
var currentLines = () => currentCart()?.lines.nodes ?? [];
|
|
2327
|
+
async function reloadAfterUnclearFailure() {
|
|
2328
|
+
const id = savedCartId();
|
|
2329
|
+
if (!id) return;
|
|
2330
|
+
try {
|
|
2331
|
+
const cart = await fetchCart(id);
|
|
2332
|
+
if (!cart) forgetSavedCart();
|
|
2333
|
+
commit(cart, "load");
|
|
2334
|
+
} catch (error) {
|
|
2335
|
+
reportError(error, "cart reload after a failed change");
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2338
|
+
function runChange(task) {
|
|
2339
|
+
return enqueue(
|
|
2340
|
+
() => task().catch(async (error) => {
|
|
2341
|
+
if (isUnclearFailure(error)) await reloadAfterUnclearFailure();
|
|
2342
|
+
throw error;
|
|
2343
|
+
})
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2142
2346
|
function changeQuantity(lineId, delta) {
|
|
2143
2347
|
const line = currentLines().find((candidate) => candidate.id === lineId);
|
|
2144
2348
|
if (!line) return Promise.resolve(currentCart());
|
|
@@ -2147,7 +2351,7 @@
|
|
|
2147
2351
|
pendingQuantities.set(lineId, job);
|
|
2148
2352
|
announce("pending");
|
|
2149
2353
|
const isLatest = () => pendingQuantities.get(lineId) === job;
|
|
2150
|
-
const sent =
|
|
2354
|
+
const sent = runChange(async () => {
|
|
2151
2355
|
if (!isLatest()) return currentCart();
|
|
2152
2356
|
const now = currentLines().find((candidate) => candidate.id === lineId);
|
|
2153
2357
|
if (now?.quantity === target) return currentCart();
|
|
@@ -2255,7 +2459,7 @@
|
|
|
2255
2459
|
const lines = (Array.isArray(input) ? input : [input]).map(
|
|
2256
2460
|
(line) => toLineInput(line, quantity, options)
|
|
2257
2461
|
);
|
|
2258
|
-
return
|
|
2462
|
+
return runChange(async () => {
|
|
2259
2463
|
const cart = await mutateCart({
|
|
2260
2464
|
name: "cartLinesAdd",
|
|
2261
2465
|
args: "$lines: [CartLineInput!]!",
|
|
@@ -2279,23 +2483,26 @@
|
|
|
2279
2483
|
const quantity = typeof patch === "number" ? patch : patch.quantity;
|
|
2280
2484
|
if (quantity !== void 0 && quantity <= 0) return Cart.removeLine(lineId);
|
|
2281
2485
|
const line = toLineUpdate(lineId, patch);
|
|
2282
|
-
return
|
|
2486
|
+
return runChange(async () => commit(await updateLines([line]), "update"));
|
|
2283
2487
|
},
|
|
2284
2488
|
changeQuantity,
|
|
2285
2489
|
/** The quantity to show: the one asked for while an update is on its way. */
|
|
2286
2490
|
quantityOf: pendingQuantityOf,
|
|
2287
2491
|
removeLine(lineIds) {
|
|
2288
2492
|
const ids = Array.isArray(lineIds) ? lineIds : [lineIds];
|
|
2289
|
-
return
|
|
2493
|
+
return runChange(async () => commit(await removeLines(ids), "remove"));
|
|
2290
2494
|
},
|
|
2495
|
+
/** Removes the lines the cart has when the change runs, so one queued just before it is cleared too. */
|
|
2291
2496
|
clear() {
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
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
|
+
});
|
|
2295
2502
|
},
|
|
2296
2503
|
setNote(note) {
|
|
2297
2504
|
const variables = { note: note ?? "" };
|
|
2298
|
-
return
|
|
2505
|
+
return runChange(
|
|
2299
2506
|
async () => commit(
|
|
2300
2507
|
await mutateCart({
|
|
2301
2508
|
name: "cartNoteUpdate",
|
|
@@ -2314,7 +2521,7 @@
|
|
|
2314
2521
|
* @param attributes The value `''` removes a key.
|
|
2315
2522
|
*/
|
|
2316
2523
|
setAttributes(attributes) {
|
|
2317
|
-
return
|
|
2524
|
+
return runChange(async () => {
|
|
2318
2525
|
const variables = { attributes: mergedAttributes(attributes) };
|
|
2319
2526
|
const cart = await mutateCart({
|
|
2320
2527
|
name: "cartAttributesUpdate",
|
|
@@ -2328,7 +2535,7 @@
|
|
|
2328
2535
|
},
|
|
2329
2536
|
/** Replaces the discount codes; `[]` removes them all. */
|
|
2330
2537
|
setDiscountCodes(codes) {
|
|
2331
|
-
return
|
|
2538
|
+
return runChange(async () => {
|
|
2332
2539
|
const variables = { discountCodes: discountCodesFrom(codes) };
|
|
2333
2540
|
const cart = await mutateCart({
|
|
2334
2541
|
name: "cartDiscountCodesUpdate",
|
|
@@ -2367,7 +2574,7 @@
|
|
|
2367
2574
|
* @param options `create: false` forgets an expired cart instead of creating an empty one.
|
|
2368
2575
|
*/
|
|
2369
2576
|
setBuyerIdentity(buyerIdentity, { create = true } = {}) {
|
|
2370
|
-
return
|
|
2577
|
+
return runChange(async () => {
|
|
2371
2578
|
const cart = await mutateCart({
|
|
2372
2579
|
name: "cartBuyerIdentityUpdate",
|
|
2373
2580
|
args: "$buyerIdentity: CartBuyerIdentityInput!",
|
|
@@ -2541,170 +2748,18 @@
|
|
|
2541
2748
|
return new Map(Object.entries(texts));
|
|
2542
2749
|
}
|
|
2543
2750
|
|
|
2544
|
-
// src/
|
|
2545
|
-
|
|
2546
|
-
var METAFIELD_BIND = "metafield:";
|
|
2547
|
-
var MULTI_LINE_METAFIELD = /^(rich_text|multi_line_text)_field$/;
|
|
2548
|
-
var ALL_PLAN_GROUPS = ["", "true", "all", "*"];
|
|
2549
|
-
var isSelect = (el) => el.tagName === "SELECT";
|
|
2550
|
-
var selectsIn = (scope, selector) => scope.$(`select${selector}`).filter(isSelect);
|
|
2551
|
-
function renderGallery(el, product) {
|
|
2552
|
-
const images = product.images.nodes;
|
|
2553
|
-
const rendered = [...el.children].map((node) => node.tagName === "IMG" ? node : node.querySelector("img"));
|
|
2554
|
-
const same = !hasTemplate(el) && rendered.length === images.length && rendered.every((img, index) => sameImage(img?.getAttribute("src"), images[index]?.url));
|
|
2555
|
-
if (same) return;
|
|
2556
|
-
repeat(el, images, (node, image) => {
|
|
2557
|
-
const target = node.matches(`img,${sel("bind", "image")}`) ? node : qsa(node, "img")[0] ?? node;
|
|
2558
|
-
setImage(target, image.url, image.altText || product.title);
|
|
2559
|
-
});
|
|
2560
|
-
}
|
|
2561
|
-
function renderMetafield(el, product, ref) {
|
|
2562
|
-
const dot = ref.indexOf(".");
|
|
2563
|
-
const namespace = ref.slice(0, dot);
|
|
2564
|
-
const key = ref.slice(dot + 1);
|
|
2565
|
-
const metafield = (product.metafields ?? []).find((field2) => field2?.namespace === namespace && field2.key === key);
|
|
2566
|
-
if (!metafield) {
|
|
2567
|
-
setText(el, "");
|
|
2568
|
-
return;
|
|
2569
|
-
}
|
|
2570
|
-
const image = metafield.reference?.image;
|
|
2571
|
-
if (image) {
|
|
2572
|
-
setImage(el, image.url, image.altText);
|
|
2573
|
-
return;
|
|
2574
|
-
}
|
|
2575
|
-
if (MULTI_LINE_METAFIELD.test(metafield.type)) el.style.whiteSpace = "pre-line";
|
|
2576
|
-
setText(el, metafield.value);
|
|
2577
|
-
}
|
|
2578
|
-
var PRODUCT_TEXTS = new Map(
|
|
2579
|
-
Object.entries({
|
|
2580
|
-
title: (product) => product.title,
|
|
2581
|
-
"description-text": (product) => product.description,
|
|
2582
|
-
vendor: (product) => product.vendor,
|
|
2583
|
-
type: (product) => product.productType,
|
|
2584
|
-
handle: (product) => product.handle,
|
|
2585
|
-
"published-at": (product) => formatDate(product.publishedAt),
|
|
2586
|
-
"min-price": (product) => formatMoney2(product.priceRange.minVariantPrice),
|
|
2587
|
-
"max-price": (product) => formatMoney2(product.priceRange.maxVariantPrice)
|
|
2588
|
-
})
|
|
2589
|
-
);
|
|
2590
|
-
function renderTags(el, product) {
|
|
2591
|
-
const exclude = (el.getAttribute("exclude") ?? "").split(",").map((tag) => tag.trim()).filter(Boolean);
|
|
2592
|
-
const tags = product.tags.filter((tag) => !exclude.includes(tag));
|
|
2593
|
-
repeat(el, tags, (node, tag) => {
|
|
2594
|
-
setText(node, tag);
|
|
2595
|
-
});
|
|
2596
|
-
}
|
|
2597
|
-
function renderProductBind(el, key, product) {
|
|
2598
|
-
const text2 = PRODUCT_TEXTS.get(key);
|
|
2599
|
-
if (text2) {
|
|
2600
|
-
setText(el, text2(product));
|
|
2601
|
-
return;
|
|
2602
|
-
}
|
|
2603
|
-
switch (key) {
|
|
2604
|
-
case "description":
|
|
2605
|
-
setStoreHtml(el, product.descriptionHtml);
|
|
2606
|
-
break;
|
|
2607
|
-
case "featured-image":
|
|
2608
|
-
setImage(el, product.featuredImage?.url, product.featuredImage?.altText || product.title);
|
|
2609
|
-
break;
|
|
2610
|
-
case "url":
|
|
2611
|
-
case "link": {
|
|
2612
|
-
const url2 = productUrl2(product.handle, product.id);
|
|
2613
|
-
if (url2) el.setAttribute("href", url2);
|
|
2614
|
-
if (key === "url" && !el.firstElementChild && !el.textContent.trim()) setText(el, product.title);
|
|
2615
|
-
break;
|
|
2616
|
-
}
|
|
2617
|
-
case "images":
|
|
2618
|
-
renderGallery(el, product);
|
|
2619
|
-
break;
|
|
2620
|
-
case "tags":
|
|
2621
|
-
renderTags(el, product);
|
|
2622
|
-
break;
|
|
2623
|
-
default:
|
|
2624
|
-
renderIndexedBind(el, key, product);
|
|
2625
|
-
}
|
|
2626
|
-
}
|
|
2627
|
-
function renderIndexedBind(el, key, product) {
|
|
2628
|
-
const nth = NTH_IMAGE_BIND.exec(key);
|
|
2629
|
-
if (nth) {
|
|
2630
|
-
const image = product.images.nodes[Number(nth[1]) - 1] ?? product.featuredImage;
|
|
2631
|
-
setImage(el, image?.url, image?.altText || product.title);
|
|
2632
|
-
} else if (key.startsWith(METAFIELD_BIND)) {
|
|
2633
|
-
renderMetafield(el, product, key.slice(METAFIELD_BIND.length));
|
|
2634
|
-
}
|
|
2635
|
-
}
|
|
2636
|
-
function renderOptionLists(scope) {
|
|
2637
|
-
const { product } = scope;
|
|
2638
|
-
scope.$(sel("option-list")).forEach((container) => {
|
|
2639
|
-
const name = scope.optName(get(container, "option-list"));
|
|
2640
|
-
const option = product.options.find((candidate) => candidate.name === name);
|
|
2641
|
-
if (!option || option.optionValues.length <= 1 && has(container, "hide-single")) {
|
|
2642
|
-
show(container, false);
|
|
2643
|
-
return;
|
|
2644
|
-
}
|
|
2645
|
-
repeat(container, option.optionValues, (node, value) => {
|
|
2646
|
-
node.setAttribute(attr("option-value"), value.name);
|
|
2647
|
-
const text2 = qsa(node, sel("bind", "option-value"))[0];
|
|
2648
|
-
if (text2) setText(text2, value.name);
|
|
2649
|
-
else if (!node.firstElementChild) setText(node, value.name);
|
|
2650
|
-
const swatchEl = qsa(node, sel("bind", "swatch"))[0];
|
|
2651
|
-
const { swatch } = value;
|
|
2652
|
-
if (!swatchEl || !swatch) return;
|
|
2653
|
-
if (swatch.color) swatchEl.style.backgroundColor = swatch.color;
|
|
2654
|
-
if (swatch.image?.previewImage) setImage(swatchEl, swatch.image.previewImage.url, value.name);
|
|
2655
|
-
});
|
|
2656
|
-
});
|
|
2657
|
-
}
|
|
2658
|
-
function renderSelects(scope) {
|
|
2659
|
-
const { product } = scope;
|
|
2660
|
-
selectsIn(scope, sel("option")).forEach((select) => {
|
|
2661
|
-
const option = product.options.find((candidate) => candidate.name === scope.optName(get(select, "option")));
|
|
2662
|
-
if (!option) {
|
|
2663
|
-
show(select, false);
|
|
2664
|
-
return;
|
|
2665
|
-
}
|
|
2666
|
-
select.replaceChildren(...option.optionValues.map((value) => new Option(value.name, value.name)));
|
|
2667
|
-
});
|
|
2668
|
-
selectsIn(scope, sel("variant-select")).forEach((select) => {
|
|
2669
|
-
select.replaceChildren(...product.variants.nodes.map((variant) => new Option(variant.title, variant.id)));
|
|
2670
|
-
});
|
|
2671
|
-
selectsIn(scope, sel("plan")).forEach((select) => {
|
|
2672
|
-
const plans = scope.plans.map((plan) => new Option(plan.name, plan.id));
|
|
2673
|
-
const oneTime = !product.requiresSellingPlan && get(select, "plan") !== "subscription-only";
|
|
2674
|
-
const oneTimeLabel = select.getAttribute(attr("one-time-label")) || "One-time purchase";
|
|
2675
|
-
select.replaceChildren(...oneTime ? [new Option(oneTimeLabel, "")] : [], ...plans);
|
|
2676
|
-
});
|
|
2677
|
-
}
|
|
2678
|
-
function renderPlanLists(scope) {
|
|
2679
|
-
scope.$(sel("plan-list")).forEach((container) => {
|
|
2680
|
-
const raw = (get(container, "plan-list") ?? "").trim();
|
|
2681
|
-
const group = ALL_PLAN_GROUPS.includes(raw.toLowerCase()) ? null : raw;
|
|
2682
|
-
const plans = scope.plans.filter((plan) => !group || plan.group.name === group);
|
|
2683
|
-
repeat(container, plans, (node, plan) => {
|
|
2684
|
-
node.setAttribute(attr("plan-value"), plan.id);
|
|
2685
|
-
if (!node.firstElementChild && !node.querySelector(sel("plan-bind"))) setText(node, plan.name);
|
|
2686
|
-
});
|
|
2687
|
-
});
|
|
2688
|
-
}
|
|
2689
|
-
function writeProduct(scope) {
|
|
2690
|
-
scope.$(sel("bind")).forEach((el) => {
|
|
2691
|
-
renderProductBind(el, get(el, "bind") ?? "", scope.product);
|
|
2692
|
-
});
|
|
2693
|
-
renderOptionLists(scope);
|
|
2694
|
-
renderSelects(scope);
|
|
2695
|
-
renderPlanLists(scope);
|
|
2696
|
-
}
|
|
2697
|
-
|
|
2698
|
-
// src/status.ts
|
|
2699
|
-
var URL_SOURCE = "url:";
|
|
2700
|
-
function decodePathSegment(segment) {
|
|
2751
|
+
// src/uri.ts
|
|
2752
|
+
function decodeOrKeep(text2) {
|
|
2701
2753
|
try {
|
|
2702
|
-
return decodeURIComponent(
|
|
2754
|
+
return decodeURIComponent(text2);
|
|
2703
2755
|
} catch (error) {
|
|
2704
|
-
if (error instanceof URIError) return
|
|
2756
|
+
if (error instanceof URIError) return text2;
|
|
2705
2757
|
throw error;
|
|
2706
2758
|
}
|
|
2707
2759
|
}
|
|
2760
|
+
|
|
2761
|
+
// src/status.ts
|
|
2762
|
+
var URL_SOURCE = "url:";
|
|
2708
2763
|
function resolveProductKey(el, location2 = window.location, name = "product") {
|
|
2709
2764
|
const raw = (get(el, name) ?? "").trim();
|
|
2710
2765
|
if (!raw.toLowerCase().startsWith(URL_SOURCE)) return { key: raw || null, fromUrl: false, raw };
|
|
@@ -2713,7 +2768,7 @@
|
|
|
2713
2768
|
if (source.toLowerCase() === "path") {
|
|
2714
2769
|
const segments = location2.pathname.split("/").filter(Boolean);
|
|
2715
2770
|
const segment = segments[segments.length - 1];
|
|
2716
|
-
key = segment === void 0 ? null :
|
|
2771
|
+
key = segment === void 0 ? null : decodeOrKeep(segment);
|
|
2717
2772
|
} else {
|
|
2718
2773
|
key = new URLSearchParams(location2.search).get(source || "handle");
|
|
2719
2774
|
}
|
|
@@ -3447,8 +3502,92 @@
|
|
|
3447
3502
|
});
|
|
3448
3503
|
}
|
|
3449
3504
|
|
|
3505
|
+
// src/markets.ts
|
|
3506
|
+
var storageKey = () => `${cfg.prefix}:market:${cfg.domain}`;
|
|
3507
|
+
var pageStorage = { getItem: (key) => getStored(key) };
|
|
3508
|
+
function parseJsonOrNull(text2) {
|
|
3509
|
+
try {
|
|
3510
|
+
return JSON.parse(text2);
|
|
3511
|
+
} catch (error) {
|
|
3512
|
+
if (error instanceof SyntaxError) return null;
|
|
3513
|
+
throw error;
|
|
3514
|
+
}
|
|
3515
|
+
}
|
|
3516
|
+
function toMarket(value) {
|
|
3517
|
+
if (typeof value !== "object" || value === null) return null;
|
|
3518
|
+
const country = toCountry(Reflect.get(value, "country"));
|
|
3519
|
+
if (!country) return null;
|
|
3520
|
+
const locale = Reflect.get(value, "locale");
|
|
3521
|
+
return {
|
|
3522
|
+
country,
|
|
3523
|
+
language: toLanguage(Reflect.get(value, "language")) ?? void 0,
|
|
3524
|
+
locale: typeof locale === "string" && locale ? locale : void 0
|
|
3525
|
+
};
|
|
3526
|
+
}
|
|
3527
|
+
var parseSavedMarket = (text2) => toMarket(parseJsonOrNull(text2));
|
|
3528
|
+
function readSavedMarket(storage = pageStorage) {
|
|
3529
|
+
const text2 = storage.getItem(storageKey());
|
|
3530
|
+
return text2 ? parseSavedMarket(text2) : null;
|
|
3531
|
+
}
|
|
3532
|
+
|
|
3533
|
+
// src/page-market.ts
|
|
3534
|
+
var configuredMarket = { country: void 0, language: void 0, locale: void 0 };
|
|
3535
|
+
var lastPageHadOwnMarket = false;
|
|
3536
|
+
function pageMarket() {
|
|
3537
|
+
const meta2 = (name) => document.querySelector(`meta[name="${cfg.prefix}-page-${name}"]`)?.getAttribute("content") || void 0;
|
|
3538
|
+
const country = toCountry(meta2("country"));
|
|
3539
|
+
if (!country) return null;
|
|
3540
|
+
return { country, language: toLanguage(meta2("language")) ?? void 0, locale: meta2("locale") };
|
|
3541
|
+
}
|
|
3542
|
+
async function visitorMarket() {
|
|
3543
|
+
const markets = cfg.markets ?? {};
|
|
3544
|
+
if (markets.detect === false) return null;
|
|
3545
|
+
if (!markets.detect) return markets.remember === false ? null : readSavedMarket();
|
|
3546
|
+
try {
|
|
3547
|
+
return (await loadPart("markets")).detectMarket(markets);
|
|
3548
|
+
} catch (error) {
|
|
3549
|
+
reportError(error, "markets");
|
|
3550
|
+
return null;
|
|
3551
|
+
}
|
|
3552
|
+
}
|
|
3553
|
+
async function applyStartMarket(overrides) {
|
|
3554
|
+
configuredMarket = { country: cfg.country, language: cfg.language, locale: cfg.locale };
|
|
3555
|
+
const page = pageMarket();
|
|
3556
|
+
lastPageHadOwnMarket = page !== null;
|
|
3557
|
+
if (page) {
|
|
3558
|
+
cfg.country = page.country;
|
|
3559
|
+
cfg.language = page.language;
|
|
3560
|
+
if (page.locale) cfg.locale = page.locale;
|
|
3561
|
+
return;
|
|
3562
|
+
}
|
|
3563
|
+
const market = await visitorMarket();
|
|
3564
|
+
if (!market) return;
|
|
3565
|
+
if (market.country) cfg.country = market.country;
|
|
3566
|
+
if (market.language) cfg.language = market.language;
|
|
3567
|
+
if (market.locale && !overrides.locale) cfg.locale = market.locale;
|
|
3568
|
+
log("market", market);
|
|
3569
|
+
}
|
|
3570
|
+
var sameCode = (a, b) => (a ?? "").toUpperCase() === (b ?? "").toUpperCase();
|
|
3571
|
+
function marketForArrival() {
|
|
3572
|
+
const page = pageMarket();
|
|
3573
|
+
const leftOwnMarket = page === null && lastPageHadOwnMarket;
|
|
3574
|
+
lastPageHadOwnMarket = page !== null;
|
|
3575
|
+
const target = page ?? (leftOwnMarket ? readSavedMarket() ?? configuredMarket : null);
|
|
3576
|
+
if (!target?.country) return null;
|
|
3577
|
+
if (sameCode(target.country, cfg.country) && sameCode(target.language, cfg.language)) return null;
|
|
3578
|
+
return { country: target.country, language: target.language, locale: target.locale };
|
|
3579
|
+
}
|
|
3580
|
+
function setCountry(code, options) {
|
|
3581
|
+
return loadPart("markets").then((part) => part.setCountry(code, options));
|
|
3582
|
+
}
|
|
3583
|
+
|
|
3450
3584
|
// src/part-integrity.ts
|
|
3451
|
-
var PART_INTEGRITY = { "tether.diagnose.js": "sha384-
|
|
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" };
|
|
3586
|
+
|
|
3587
|
+
// src/part-registry.ts
|
|
3588
|
+
function isTetherInternals(value) {
|
|
3589
|
+
return isRecord(value) && isRecord(value["__core"]) && isRecord(value["__parts"]);
|
|
3590
|
+
}
|
|
3452
3591
|
|
|
3453
3592
|
// src/lazy-iife.ts
|
|
3454
3593
|
var coreModules = {
|
|
@@ -3504,6 +3643,9 @@
|
|
|
3504
3643
|
get "market-codes.ts"() {
|
|
3505
3644
|
return [toCountry, toLanguage];
|
|
3506
3645
|
},
|
|
3646
|
+
get "page-market.ts"() {
|
|
3647
|
+
return [setCountry];
|
|
3648
|
+
},
|
|
3507
3649
|
get "shopify-values.ts"() {
|
|
3508
3650
|
return [isHttpsUrl];
|
|
3509
3651
|
},
|
|
@@ -3533,14 +3675,15 @@
|
|
|
3533
3675
|
script.async = true;
|
|
3534
3676
|
applyIntegrity(script, integrity);
|
|
3535
3677
|
script.onload = () => {
|
|
3536
|
-
const
|
|
3537
|
-
const part =
|
|
3678
|
+
const tether = Reflect.get(window, "Tether");
|
|
3679
|
+
const part = isTetherInternals(tether) ? tether.__parts[name] : void 0;
|
|
3538
3680
|
if (part) {
|
|
3539
3681
|
resolve(part);
|
|
3540
3682
|
return;
|
|
3541
3683
|
}
|
|
3542
3684
|
parts.delete(name);
|
|
3543
|
-
|
|
3685
|
+
const problem = isTetherInternals(tether) ? "did not register" : "can't register: window.Tether is no longer this Tether script's, another script replaced it";
|
|
3686
|
+
reject(new Error(`${url2} ${problem}`));
|
|
3544
3687
|
};
|
|
3545
3688
|
script.onerror = () => {
|
|
3546
3689
|
parts.delete(name);
|
|
@@ -3574,84 +3717,6 @@
|
|
|
3574
3717
|
return loading;
|
|
3575
3718
|
}
|
|
3576
3719
|
|
|
3577
|
-
// src/markets.ts
|
|
3578
|
-
var storageKey = () => `${cfg.prefix}:market:${cfg.domain}`;
|
|
3579
|
-
var pageStorage = { getItem: (key) => getStored(key) };
|
|
3580
|
-
function parseJsonOrNull(text2) {
|
|
3581
|
-
try {
|
|
3582
|
-
return JSON.parse(text2);
|
|
3583
|
-
} catch (error) {
|
|
3584
|
-
if (error instanceof SyntaxError) return null;
|
|
3585
|
-
throw error;
|
|
3586
|
-
}
|
|
3587
|
-
}
|
|
3588
|
-
function toMarket(value) {
|
|
3589
|
-
if (typeof value !== "object" || value === null) return null;
|
|
3590
|
-
const country = toCountry(Reflect.get(value, "country"));
|
|
3591
|
-
if (!country) return null;
|
|
3592
|
-
const locale = Reflect.get(value, "locale");
|
|
3593
|
-
return {
|
|
3594
|
-
country,
|
|
3595
|
-
language: toLanguage(Reflect.get(value, "language")) ?? void 0,
|
|
3596
|
-
locale: typeof locale === "string" && locale ? locale : void 0
|
|
3597
|
-
};
|
|
3598
|
-
}
|
|
3599
|
-
function readSavedMarket(storage = pageStorage) {
|
|
3600
|
-
const text2 = storage.getItem(storageKey());
|
|
3601
|
-
return text2 ? toMarket(parseJsonOrNull(text2)) : null;
|
|
3602
|
-
}
|
|
3603
|
-
|
|
3604
|
-
// src/page-market.ts
|
|
3605
|
-
var configuredMarket = { country: void 0, language: void 0, locale: void 0 };
|
|
3606
|
-
var lastPageHadOwnMarket = false;
|
|
3607
|
-
function pageMarket() {
|
|
3608
|
-
const meta2 = (name) => document.querySelector(`meta[name="${cfg.prefix}-page-${name}"]`)?.getAttribute("content") || void 0;
|
|
3609
|
-
const country = toCountry(meta2("country"));
|
|
3610
|
-
if (!country) return null;
|
|
3611
|
-
return { country, language: toLanguage(meta2("language")) ?? void 0, locale: meta2("locale") };
|
|
3612
|
-
}
|
|
3613
|
-
async function visitorMarket() {
|
|
3614
|
-
const markets = cfg.markets ?? {};
|
|
3615
|
-
if (markets.detect === false) return null;
|
|
3616
|
-
if (!markets.detect) return markets.remember === false ? null : readSavedMarket();
|
|
3617
|
-
try {
|
|
3618
|
-
return (await loadPart("markets")).detectMarket(markets);
|
|
3619
|
-
} catch (error) {
|
|
3620
|
-
reportError(error, "markets");
|
|
3621
|
-
return null;
|
|
3622
|
-
}
|
|
3623
|
-
}
|
|
3624
|
-
async function applyStartMarket(overrides) {
|
|
3625
|
-
configuredMarket = { country: cfg.country, language: cfg.language, locale: cfg.locale };
|
|
3626
|
-
const page = pageMarket();
|
|
3627
|
-
lastPageHadOwnMarket = page !== null;
|
|
3628
|
-
if (page) {
|
|
3629
|
-
cfg.country = page.country;
|
|
3630
|
-
cfg.language = page.language;
|
|
3631
|
-
if (page.locale) cfg.locale = page.locale;
|
|
3632
|
-
return;
|
|
3633
|
-
}
|
|
3634
|
-
const market = await visitorMarket();
|
|
3635
|
-
if (!market) return;
|
|
3636
|
-
if (market.country) cfg.country = market.country;
|
|
3637
|
-
if (market.language) cfg.language = market.language;
|
|
3638
|
-
if (market.locale && !overrides.locale) cfg.locale = market.locale;
|
|
3639
|
-
log("market", market);
|
|
3640
|
-
}
|
|
3641
|
-
var sameCode = (a, b) => (a ?? "").toUpperCase() === (b ?? "").toUpperCase();
|
|
3642
|
-
function marketForArrival() {
|
|
3643
|
-
const page = pageMarket();
|
|
3644
|
-
const leftOwnMarket = page === null && lastPageHadOwnMarket;
|
|
3645
|
-
lastPageHadOwnMarket = page !== null;
|
|
3646
|
-
const target = page ?? (leftOwnMarket ? readSavedMarket() ?? configuredMarket : null);
|
|
3647
|
-
if (!target?.country) return null;
|
|
3648
|
-
if (sameCode(target.country, cfg.country) && sameCode(target.language, cfg.language)) return null;
|
|
3649
|
-
return { country: target.country, language: target.language, locale: target.locale };
|
|
3650
|
-
}
|
|
3651
|
-
function setCountry(code, options) {
|
|
3652
|
-
return loadPart("markets").then((part) => part.setCountry(code, options));
|
|
3653
|
-
}
|
|
3654
|
-
|
|
3655
3720
|
// src/account-marks.ts
|
|
3656
3721
|
var hintKey = () => `${cfg.prefix}:account-hint:${cfg.domain}`;
|
|
3657
3722
|
function removeFromBothAreas(key) {
|
|
@@ -3951,7 +4016,7 @@
|
|
|
3951
4016
|
}
|
|
3952
4017
|
|
|
3953
4018
|
// src/index.ts
|
|
3954
|
-
var VERSION = "0.7.
|
|
4019
|
+
var VERSION = "0.7.2";
|
|
3955
4020
|
var OBSERVE_DEBOUNCE_MS = 50;
|
|
3956
4021
|
function pageConfig() {
|
|
3957
4022
|
const config = pageGlobal("TETHER_CONFIG");
|