@occluze/tether 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -5
- package/dist/esm/astro.js +2 -0
- package/dist/esm/chunks/chunk-HHMOPDZG.js +1 -0
- package/dist/esm/chunks/chunk-HXKV6QGB.js +4 -0
- package/dist/esm/chunks/{chunk-KYQ5OGBX.js → chunk-RBVRRQIU.js} +1 -1
- package/dist/esm/chunks/chunk-RNZMTQTX.js +1 -0
- package/dist/esm/chunks/chunk-VC46IEJQ.js +1 -0
- package/dist/esm/chunks/diagnose-J667GHGS.js +1 -0
- package/dist/esm/chunks/markets-ui-EGWIEUOZ.js +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +1 -0
- package/dist/tether.diagnose.js +4 -3
- package/dist/tether.diagnose.min.js +1 -1
- package/dist/tether.js +229 -61
- package/dist/tether.markets.js +16 -10
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +3 -3
- package/dist/tether.min.js.map +3 -3
- package/package.json +27 -4
- package/src/astro.js +73 -0
- package/src/cart.js +47 -7
- package/src/client.js +25 -15
- package/src/config.js +2 -0
- package/src/diagnose.js +1 -1
- package/src/dom.js +32 -9
- package/src/fragments.js +12 -11
- package/src/index.js +18 -6
- package/src/initial.js +66 -0
- package/src/loader.js +49 -6
- package/src/markets-ui.js +13 -5
- package/src/markets.js +1 -1
- package/src/mounting.js +42 -2
- package/src/product.js +33 -9
- package/src/server.js +179 -0
- package/src/status.js +1 -6
- package/types/astro.d.ts +15 -0
- package/types/index.d.ts +17 -2
- package/types/server.d.ts +94 -0
- package/dist/esm/chunks/chunk-GHIUMD5H.js +0 -1
- package/dist/esm/chunks/chunk-LI3YDMGF.js +0 -4
- package/dist/esm/chunks/diagnose-ZXXXJIQE.js +0 -1
- package/dist/esm/chunks/markets-ui-AG6Y5VUE.js +0 -1
package/dist/tether.js
CHANGED
|
@@ -46,6 +46,8 @@
|
|
|
46
46
|
sellingPlansFirst: 25,
|
|
47
47
|
collectionFirst: 24,
|
|
48
48
|
retries: 2,
|
|
49
|
+
revalidate: true,
|
|
50
|
+
// products from the server (tether/server hydrate()): refresh once in the background
|
|
49
51
|
// Cart
|
|
50
52
|
storageKey: null,
|
|
51
53
|
// defaults to `tether:cart:<domain>`
|
|
@@ -63,6 +65,8 @@
|
|
|
63
65
|
// mount document on DOMContentLoaded
|
|
64
66
|
observe: false,
|
|
65
67
|
// auto-mount nodes added later (CMS load-more, filters)
|
|
68
|
+
astro: true,
|
|
69
|
+
// mount on Astro View Transitions (astro:page-load); false if you do it yourself
|
|
66
70
|
partsUrl: null,
|
|
67
71
|
// folder of tether.markets/diagnose(.min).js for the <script> build (default: next to it)
|
|
68
72
|
// Formatting
|
|
@@ -117,6 +121,7 @@
|
|
|
117
121
|
TetherError: () => TetherError,
|
|
118
122
|
endpoint: () => endpoint,
|
|
119
123
|
gql: () => gql,
|
|
124
|
+
request: () => request,
|
|
120
125
|
unwrap: () => unwrap,
|
|
121
126
|
withContext: () => withContext
|
|
122
127
|
});
|
|
@@ -130,39 +135,38 @@
|
|
|
130
135
|
this.userErrors = userErrors;
|
|
131
136
|
}
|
|
132
137
|
};
|
|
133
|
-
function endpoint() {
|
|
134
|
-
return `https://${
|
|
138
|
+
function endpoint(conf = cfg) {
|
|
139
|
+
return `https://${conf.domain}/api/${conf.apiVersion}/graphql.json`;
|
|
135
140
|
}
|
|
136
141
|
function withContext(query, ctx = cfg) {
|
|
137
142
|
const args = [];
|
|
138
143
|
if (ctx.country) args.push(`country: ${String(ctx.country).toUpperCase()}`);
|
|
139
144
|
if (ctx.language) args.push(`language: ${String(ctx.language).toUpperCase().replace("-", "_")}`);
|
|
140
145
|
if (!args.length || /@inContext/.test(query)) return query;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
);
|
|
146
|
+
const tag = `@inContext(${args.join(", ")})`;
|
|
147
|
+
if (/^\s*\{/.test(query)) return `query ${tag} ${query.trimStart()}`;
|
|
148
|
+
return query.replace(/^(\s*(?:query|mutation)(?:\s+\w+)?\s*(?:\([^)]*\))?)/, `$1 ${tag}`);
|
|
145
149
|
}
|
|
146
150
|
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
147
|
-
async function
|
|
148
|
-
if (!
|
|
151
|
+
async function request(conf, query, variables = {}, { context = true } = {}) {
|
|
152
|
+
if (!conf.domain || !conf.token) {
|
|
149
153
|
throw new TetherError("Tether is not configured: domain and token are required", { code: "NOT_CONFIGURED" });
|
|
150
154
|
}
|
|
151
|
-
const body = JSON.stringify({ query: context ? withContext(query) : query, variables });
|
|
155
|
+
const body = JSON.stringify({ query: context ? withContext(query, conf) : query, variables });
|
|
152
156
|
let attempt = 0;
|
|
153
157
|
for (; ; ) {
|
|
154
158
|
let res;
|
|
155
159
|
try {
|
|
156
|
-
res = await fetch(endpoint(), {
|
|
160
|
+
res = await (conf.fetch || fetch)(endpoint(conf), {
|
|
157
161
|
method: "POST",
|
|
158
162
|
headers: {
|
|
159
163
|
"Content-Type": "application/json",
|
|
160
|
-
"X-Shopify-Storefront-Access-Token":
|
|
164
|
+
"X-Shopify-Storefront-Access-Token": conf.token
|
|
161
165
|
},
|
|
162
166
|
body
|
|
163
167
|
});
|
|
164
168
|
} catch (err) {
|
|
165
|
-
if (attempt++ <
|
|
169
|
+
if (attempt++ < conf.retries) {
|
|
166
170
|
await sleep(300 * 2 ** attempt);
|
|
167
171
|
continue;
|
|
168
172
|
}
|
|
@@ -170,7 +174,7 @@
|
|
|
170
174
|
}
|
|
171
175
|
if (res.status === 430) throw new TetherError("Shopify security rejection (430)", { status: 430 });
|
|
172
176
|
const retryable = res.status === 429 || res.status >= 500;
|
|
173
|
-
if (retryable && attempt++ <
|
|
177
|
+
if (retryable && attempt++ < conf.retries) {
|
|
174
178
|
log("retry", res.status, attempt);
|
|
175
179
|
await sleep(300 * 2 ** attempt);
|
|
176
180
|
continue;
|
|
@@ -185,10 +189,13 @@
|
|
|
185
189
|
const inventoryDenied = json.errors.some((e) => /quantityAvailable/.test(e.message || ""));
|
|
186
190
|
if (inventoryDenied && /\bquantityAvailable\b/.test(body)) {
|
|
187
191
|
log("no inventory permission, retrying without quantityAvailable");
|
|
188
|
-
return
|
|
192
|
+
return request(conf, query.replace(/\bquantityAvailable\b/g, ""), variables, { context });
|
|
193
|
+
}
|
|
194
|
+
if (json.errors.some((e) => /'warnings'/.test(e.message || "")) && /warnings \{ code message \}/.test(query)) {
|
|
195
|
+
return request(conf, query.replace(/ warnings \{ code message \}/g, ""), variables, { context });
|
|
189
196
|
}
|
|
190
197
|
const throttled = json.errors.some((e) => e.extensions?.code === "THROTTLED");
|
|
191
|
-
if (throttled && attempt++ <
|
|
198
|
+
if (throttled && attempt++ < conf.retries) {
|
|
192
199
|
await sleep(500 * 2 ** attempt);
|
|
193
200
|
continue;
|
|
194
201
|
}
|
|
@@ -201,6 +208,7 @@
|
|
|
201
208
|
return json.data;
|
|
202
209
|
}
|
|
203
210
|
}
|
|
211
|
+
var gql = (query, variables, opts) => request(cfg, query, variables, opts);
|
|
204
212
|
function unwrap(payload, name) {
|
|
205
213
|
if (!payload) throw new TetherError(`${name}: empty response`);
|
|
206
214
|
const errs = payload.userErrors || [];
|
|
@@ -230,8 +238,8 @@
|
|
|
230
238
|
});
|
|
231
239
|
var MONEY = "amount currencyCode";
|
|
232
240
|
var IMAGE = "url altText width height";
|
|
233
|
-
function metafieldIdentifiers() {
|
|
234
|
-
return (
|
|
241
|
+
function metafieldIdentifiers(o = cfg) {
|
|
242
|
+
return (o.metafields || []).map((m) => {
|
|
235
243
|
if (typeof m === "string") {
|
|
236
244
|
const i = m.indexOf(".");
|
|
237
245
|
return { namespace: m.slice(0, i), key: m.slice(i + 1) };
|
|
@@ -239,16 +247,16 @@
|
|
|
239
247
|
return m;
|
|
240
248
|
}).filter((m) => m.namespace && m.key);
|
|
241
249
|
}
|
|
242
|
-
function metafieldsField() {
|
|
243
|
-
const ids = metafieldIdentifiers();
|
|
250
|
+
function metafieldsField(o = cfg) {
|
|
251
|
+
const ids = metafieldIdentifiers(o);
|
|
244
252
|
if (!ids.length) return "";
|
|
245
253
|
const list = ids.map((m) => `{namespace: ${JSON.stringify(m.namespace)}, key: ${JSON.stringify(m.key)}}`).join(", ");
|
|
246
254
|
return `metafields(identifiers:[${list}]){namespace key type value reference{... on MediaImage{image{${IMAGE}}}}}`;
|
|
247
255
|
}
|
|
248
256
|
var SELLING_PLAN_FIELDS = ` id name description recurringDeliveries options{name value}priceAdjustments{orderCount adjustmentValue{__typename ... on SellingPlanPercentagePriceAdjustment{adjustmentPercentage}... on SellingPlanFixedAmountPriceAdjustment{adjustmentAmount{${MONEY}}}... on SellingPlanFixedPriceAdjustment{price{${MONEY}}}}}`;
|
|
249
257
|
var ALLOCATION_FIELDS = ` sellingPlan{id}checkoutChargeAmount{${MONEY}}remainingBalanceChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}unitPrice{${MONEY}}}`;
|
|
250
|
-
function productFragment() {
|
|
251
|
-
return ` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${IMAGE}}images(first:${
|
|
258
|
+
function productFragment(o = cfg) {
|
|
259
|
+
return ` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${IMAGE}}images(first:${o.imagesFirst}){nodes{${IMAGE}}}options{name optionValues{name swatch{color image{previewImage{${IMAGE}}}}}}priceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}compareAtPriceRange{minVariantPrice{${MONEY}}maxVariantPrice{${MONEY}}}${metafieldsField(o)} sellingPlanGroups(first:${o.sellingPlanGroupsFirst}){nodes{name appName options{name values}sellingPlans(first:${o.sellingPlansFirst}){nodes{${SELLING_PLAN_FIELDS}}}}}variants(first:${o.variantsFirst}){nodes{id title availableForSale currentlyNotInStock quantityAvailable sku barcode selectedOptions{name value}image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}unitPrice{${MONEY}}sellingPlanAllocations(first:${o.sellingPlansFirst}){nodes{${ALLOCATION_FIELDS}}}}}}`;
|
|
252
260
|
}
|
|
253
261
|
function cartFragment() {
|
|
254
262
|
return ` fragment TetherCart on Cart{id checkoutUrl totalQuantity note createdAt updatedAt attributes{key value}buyerIdentity{countryCode email}discountCodes{code applicable}cost{subtotalAmount{${MONEY}}totalAmount{${MONEY}}checkoutChargeAmount{${MONEY}}}lines(first:100){nodes{id quantity attributes{key value}cost{totalAmount{${MONEY}}subtotalAmount{${MONEY}}amountPerQuantity{${MONEY}}compareAtAmountPerQuantity{${MONEY}}}discountAllocations{discountedAmount{${MONEY}}}sellingPlanAllocation{sellingPlan{id name description recurringDeliveries options{name value}}checkoutChargeAmount{${MONEY}}priceAdjustments{price{${MONEY}}compareAtPrice{${MONEY}}perDeliveryPrice{${MONEY}}}}merchandise{... on ProductVariant{id title sku availableForSale requiresShipping image{${IMAGE}}price{${MONEY}}compareAtPrice{${MONEY}}selectedOptions{name value}product{id title handle vendor featuredImage{${IMAGE}}}}}... on ComponentizableCartLine{lineComponents{id quantity merchandise{... on ProductVariant{id title product{title handle}}}}}}}}`;
|
|
@@ -267,8 +275,11 @@
|
|
|
267
275
|
getProduct: () => getProduct,
|
|
268
276
|
getProducts: () => getProducts,
|
|
269
277
|
getRecommendations: () => getRecommendations,
|
|
278
|
+
hydrateProducts: () => hydrateProducts,
|
|
270
279
|
normalizeProductKey: () => normalizeProductKey,
|
|
271
|
-
primeProduct: () => primeProduct
|
|
280
|
+
primeProduct: () => primeProduct,
|
|
281
|
+
refreshProducts: () => refreshProducts,
|
|
282
|
+
takeStale: () => takeStale
|
|
272
283
|
});
|
|
273
284
|
var productCache = /* @__PURE__ */ new Map();
|
|
274
285
|
var pending = [];
|
|
@@ -343,8 +354,13 @@
|
|
|
343
354
|
});
|
|
344
355
|
keys.forEach((k) => settle(k, (r) => {
|
|
345
356
|
if (got.has(k)) {
|
|
346
|
-
|
|
347
|
-
|
|
357
|
+
const p = got.get(k);
|
|
358
|
+
if (gen === generation) {
|
|
359
|
+
primeProduct(p);
|
|
360
|
+
freshAt.set(p.handle, Date.now());
|
|
361
|
+
freshAt.set(p.id, Date.now());
|
|
362
|
+
}
|
|
363
|
+
r.resolve(p);
|
|
348
364
|
} else {
|
|
349
365
|
if (gen === generation) productCache.delete(k);
|
|
350
366
|
r.reject(new TetherError(`Product not found: ${k}`, { code: "NOT_FOUND" }));
|
|
@@ -360,6 +376,35 @@
|
|
|
360
376
|
function clearProductCache() {
|
|
361
377
|
productCache.clear();
|
|
362
378
|
generation++;
|
|
379
|
+
stale.clear();
|
|
380
|
+
freshAt.clear();
|
|
381
|
+
}
|
|
382
|
+
var stale = /* @__PURE__ */ new Set();
|
|
383
|
+
var freshAt = /* @__PURE__ */ new Map();
|
|
384
|
+
var FRESH_MS = 6e4;
|
|
385
|
+
function hydrateProducts(products) {
|
|
386
|
+
(products || []).forEach((p) => {
|
|
387
|
+
if (!p?.id || !p.handle) return;
|
|
388
|
+
if (Date.now() - (freshAt.get(p.handle) || 0) < FRESH_MS && productCache.has(p.handle)) return;
|
|
389
|
+
primeProduct(p);
|
|
390
|
+
stale.add(p.handle);
|
|
391
|
+
stale.add(p.id);
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
function takeStale(key) {
|
|
395
|
+
const k = normalizeProductKey(key);
|
|
396
|
+
if (!stale.has(k)) return false;
|
|
397
|
+
const p = productCache.get(k);
|
|
398
|
+
stale.delete(k);
|
|
399
|
+
p?.then?.((x) => {
|
|
400
|
+
stale.delete(x.handle);
|
|
401
|
+
stale.delete(x.id);
|
|
402
|
+
});
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
function refreshProducts(keys) {
|
|
406
|
+
keys.forEach((k) => productCache.delete(normalizeProductKey(k)));
|
|
407
|
+
return keys.map((k) => getProduct(k));
|
|
363
408
|
}
|
|
364
409
|
async function getProducts({
|
|
365
410
|
collection,
|
|
@@ -389,8 +434,10 @@
|
|
|
389
434
|
data.products.nodes.forEach(primeProduct);
|
|
390
435
|
return { collection: null, products: data.products.nodes, pageInfo: data.products.pageInfo };
|
|
391
436
|
}
|
|
392
|
-
async function getRecommendations(
|
|
393
|
-
const
|
|
437
|
+
async function getRecommendations(product, intent = "RELATED") {
|
|
438
|
+
const key = normalizeProductKey(typeof product === "object" ? product?.id : product);
|
|
439
|
+
const byId = key?.startsWith("gid://");
|
|
440
|
+
const data = await gql(`query TetherRecs(${byId ? "$id: ID!" : "$handle: String!"},$intent:ProductRecommendationIntent){productRecommendations(${byId ? "productId: $id" : "productHandle: $handle"},intent:$intent){...TetherProduct}}${productFragment()}`, byId ? { id: key, intent } : { handle: key, intent });
|
|
394
441
|
const list = data.productRecommendations || [];
|
|
395
442
|
list.forEach(primeProduct);
|
|
396
443
|
return list;
|
|
@@ -492,6 +539,7 @@
|
|
|
492
539
|
get: () => get,
|
|
493
540
|
has: () => has,
|
|
494
541
|
hasTemplate: () => hasTemplate,
|
|
542
|
+
imageUrl: () => imageUrl,
|
|
495
543
|
notifyLayoutChanged: () => notifyLayoutChanged,
|
|
496
544
|
parseJSONAttr: () => parseJSONAttr,
|
|
497
545
|
productUrl: () => productUrl,
|
|
@@ -499,6 +547,7 @@
|
|
|
499
547
|
readText: () => readText,
|
|
500
548
|
repeat: () => repeat,
|
|
501
549
|
repeatKeyed: () => repeatKeyed,
|
|
550
|
+
sameImage: () => sameImage,
|
|
502
551
|
scoped: () => scoped,
|
|
503
552
|
sel: () => sel,
|
|
504
553
|
setImage: () => setImage,
|
|
@@ -547,25 +596,31 @@
|
|
|
547
596
|
return "";
|
|
548
597
|
}
|
|
549
598
|
}
|
|
550
|
-
function
|
|
551
|
-
if (!url
|
|
552
|
-
const w = el.getAttribute("max-width");
|
|
553
|
-
const h = el.getAttribute("max-height");
|
|
554
|
-
const crop = el.getAttribute("crop");
|
|
555
|
-
if (!w && !h) return url;
|
|
599
|
+
function imageUrl(url, { width, height, crop } = {}) {
|
|
600
|
+
if (!url) return url;
|
|
556
601
|
try {
|
|
557
602
|
const u = new URL(url);
|
|
558
|
-
if (
|
|
559
|
-
if (
|
|
603
|
+
if (width) u.searchParams.set("width", String(width));
|
|
604
|
+
if (height) u.searchParams.set("height", String(height));
|
|
560
605
|
if (crop) u.searchParams.set("crop", crop);
|
|
561
606
|
return u.toString();
|
|
562
607
|
} catch {
|
|
563
608
|
return url;
|
|
564
609
|
}
|
|
565
610
|
}
|
|
611
|
+
function cdnUrl(url, el) {
|
|
612
|
+
if (!url || !el) return url;
|
|
613
|
+
const g = (k) => el.getAttribute(k);
|
|
614
|
+
return imageUrl(url, { width: g("max-width"), height: g("max-height"), crop: g("max-width") || g("max-height") ? g("crop") : null });
|
|
615
|
+
}
|
|
616
|
+
var sameImage = (a, b) => Boolean(a && b) && a.split("?")[0] === b.split("?")[0];
|
|
566
617
|
function setImage(el, url, alt) {
|
|
567
618
|
if (!url) return;
|
|
568
619
|
const src = cdnUrl(url, el);
|
|
620
|
+
if (el.tagName === "IMG" && sameImage(el.getAttribute("src"), src)) {
|
|
621
|
+
if (alt != null && !el.getAttribute("alt")) el.setAttribute("alt", alt);
|
|
622
|
+
return;
|
|
623
|
+
}
|
|
569
624
|
if (el.tagName === "IMG") {
|
|
570
625
|
el.removeAttribute("srcset");
|
|
571
626
|
el.removeAttribute("sizes");
|
|
@@ -597,14 +652,21 @@
|
|
|
597
652
|
function show(el, visible) {
|
|
598
653
|
const k = "tetherPrevDisplay";
|
|
599
654
|
if (visible) {
|
|
655
|
+
if (el.dataset.tetherShown !== void 0) return;
|
|
656
|
+
el.dataset.tetherShown = "";
|
|
657
|
+
el.removeAttribute("hidden");
|
|
600
658
|
if (el.dataset[k] !== void 0) {
|
|
601
659
|
el.style.display = el.dataset[k];
|
|
602
660
|
delete el.dataset[k];
|
|
603
661
|
} else if (el.style.display === "none") {
|
|
604
662
|
el.style.display = "";
|
|
605
663
|
}
|
|
664
|
+
if (typeof getComputedStyle === "function" && getComputedStyle(el).display === "none") {
|
|
665
|
+
el.style.display = el.getAttribute(`${cfg.prefix}-display`) || "block";
|
|
666
|
+
}
|
|
606
667
|
} else {
|
|
607
|
-
|
|
668
|
+
delete el.dataset.tetherShown;
|
|
669
|
+
if (el.dataset[k] === void 0 && el.style.display !== "none") el.dataset[k] = el.style.display || "";
|
|
608
670
|
el.style.display = "none";
|
|
609
671
|
}
|
|
610
672
|
}
|
|
@@ -752,7 +814,7 @@
|
|
|
752
814
|
return line;
|
|
753
815
|
}
|
|
754
816
|
function mutation(name, args, inner) {
|
|
755
|
-
return `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}}}${cartFragment()}`;
|
|
817
|
+
return `mutation Tether_${name}(${args}){${name}(${inner}){cart{...TetherCart}userErrors{field message}warnings{code message}}}${cartFragment()}`;
|
|
756
818
|
}
|
|
757
819
|
var FOCUSABLE = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
|
|
758
820
|
var returnFocus = null;
|
|
@@ -791,6 +853,16 @@
|
|
|
791
853
|
return this.state?.totalQuantity || 0;
|
|
792
854
|
},
|
|
793
855
|
lastError: null,
|
|
856
|
+
/** Shopify's notes on the last change, e.g. less stock than asked: [{ code, message }]. */
|
|
857
|
+
warnings: [],
|
|
858
|
+
/** true once the saved cart (or the absence of one) is known. */
|
|
859
|
+
loaded: false,
|
|
860
|
+
_take(payload, name) {
|
|
861
|
+
const p = unwrap(payload, name);
|
|
862
|
+
this.warnings = p.warnings || [];
|
|
863
|
+
if (this.warnings.length) emit("cart-warning", { warnings: this.warnings });
|
|
864
|
+
return p.cart;
|
|
865
|
+
},
|
|
794
866
|
_queue(fn) {
|
|
795
867
|
const run = this._chain.then(fn, fn).then((value) => {
|
|
796
868
|
if (this.lastError) {
|
|
@@ -819,7 +891,9 @@
|
|
|
819
891
|
const id = this.id();
|
|
820
892
|
if (!id) {
|
|
821
893
|
this.state = null;
|
|
894
|
+
this.loaded = true;
|
|
822
895
|
this.render();
|
|
896
|
+
emit("cart-updated", { cart: null, reason: "load" });
|
|
823
897
|
return null;
|
|
824
898
|
}
|
|
825
899
|
try {
|
|
@@ -834,7 +908,9 @@
|
|
|
834
908
|
if (isMissingCart(err)) this._setId(null);
|
|
835
909
|
this.state = null;
|
|
836
910
|
}
|
|
911
|
+
this.loaded = true;
|
|
837
912
|
this.render();
|
|
913
|
+
emit("cart-updated", { cart: this.state, reason: "load" });
|
|
838
914
|
return this.state;
|
|
839
915
|
},
|
|
840
916
|
async _create(input = {}) {
|
|
@@ -843,7 +919,7 @@
|
|
|
843
919
|
mutation("cartCreate", "$input: CartInput!", "input: $input"),
|
|
844
920
|
{ input: { buyerIdentity, ...input } }
|
|
845
921
|
);
|
|
846
|
-
return
|
|
922
|
+
return this._take(data.cartCreate, "cartCreate");
|
|
847
923
|
},
|
|
848
924
|
/** Runs a cart mutation; creates a fresh cart if ours expired. */
|
|
849
925
|
async _mutate(name, args, inner, variables, createInput) {
|
|
@@ -851,7 +927,7 @@
|
|
|
851
927
|
if (!cartId) return createInput ? this._create(createInput) : null;
|
|
852
928
|
try {
|
|
853
929
|
const data = await gql(mutation(name, `$cartId: ID!, ${args}`, `cartId: $cartId, ${inner}`), { ...variables, cartId });
|
|
854
|
-
return
|
|
930
|
+
return this._take(data[name], name);
|
|
855
931
|
} catch (err) {
|
|
856
932
|
if (createInput && isMissingCart(err)) {
|
|
857
933
|
log("cart expired, creating a new one");
|
|
@@ -935,7 +1011,8 @@
|
|
|
935
1011
|
return this.setDiscountCodes([.../* @__PURE__ */ new Set([...current, code])]);
|
|
936
1012
|
},
|
|
937
1013
|
removeDiscount(code) {
|
|
938
|
-
|
|
1014
|
+
const k = String(code).toLowerCase();
|
|
1015
|
+
return this.setDiscountCodes((this.state?.discountCodes || []).map((d) => d.code).filter((c) => c.toLowerCase() !== k));
|
|
939
1016
|
},
|
|
940
1017
|
/** { countryCode, email, phone } — changes currency/prices for that market. */
|
|
941
1018
|
setBuyerIdentity(buyerIdentity, { create = true } = {}) {
|
|
@@ -955,7 +1032,7 @@
|
|
|
955
1032
|
*/
|
|
956
1033
|
async checkout(target = cfg.checkoutTarget) {
|
|
957
1034
|
const id = this.id();
|
|
958
|
-
if (!id) return null;
|
|
1035
|
+
if (!id || this.state && !this.count) return null;
|
|
959
1036
|
const tab = target === "_blank" ? window.open("", "_blank") : null;
|
|
960
1037
|
if (tab) {
|
|
961
1038
|
try {
|
|
@@ -1021,6 +1098,17 @@
|
|
|
1021
1098
|
if (this.isOpen()) this.close();
|
|
1022
1099
|
else this.open();
|
|
1023
1100
|
},
|
|
1101
|
+
/**
|
|
1102
|
+
* For framework islands (React, Svelte, Vue): fn(cart, { open }) now and on every change.
|
|
1103
|
+
* Returns a function that stops listening.
|
|
1104
|
+
*/
|
|
1105
|
+
subscribe(fn) {
|
|
1106
|
+
const call = () => fn(this.state, { open: this.isOpen(), count: this.count, loaded: this.loaded, warnings: this.warnings });
|
|
1107
|
+
const h = () => call();
|
|
1108
|
+
["cart-updated", "cart-toggled"].forEach((e) => document.addEventListener(`${cfg.prefix}:${e}`, h));
|
|
1109
|
+
call();
|
|
1110
|
+
return () => ["cart-updated", "cart-toggled"].forEach((e) => document.removeEventListener(`${cfg.prefix}:${e}`, h));
|
|
1111
|
+
},
|
|
1024
1112
|
// -------------------------------------------------------------- render
|
|
1025
1113
|
render() {
|
|
1026
1114
|
const cart = this.state;
|
|
@@ -1038,12 +1126,18 @@
|
|
|
1038
1126
|
"cart-has-subscription": hasSub,
|
|
1039
1127
|
"cart-no-subscription": !hasSub,
|
|
1040
1128
|
"cart-has-discount": (cart?.discountCodes || []).some((d) => d.applicable),
|
|
1041
|
-
"cart-error": Boolean(this.lastError)
|
|
1129
|
+
"cart-error": Boolean(this.lastError),
|
|
1130
|
+
"cart-warning": this.warnings.length > 0
|
|
1042
1131
|
};
|
|
1132
|
+
qsa(document, sel("cart-warning")).forEach((el) => setTextTracked(el, [...new Set(this.warnings.map((w) => w.message))].join(" ")));
|
|
1043
1133
|
qsa(document, sel("show")).forEach((el) => {
|
|
1044
1134
|
const k = get(el, "show");
|
|
1045
1135
|
if (k in conds && !el.closest(sel("product"))) show(el, conds[k]);
|
|
1046
1136
|
});
|
|
1137
|
+
qsa(document, sel("checkout")).forEach((el) => {
|
|
1138
|
+
el.classList.toggle("is-disabled", !count);
|
|
1139
|
+
el.setAttribute("aria-disabled", String(!count));
|
|
1140
|
+
});
|
|
1047
1141
|
qsa(document, `textarea${sel("cart-note")},input${sel("cart-note")}`).forEach((el) => {
|
|
1048
1142
|
if (document.activeElement !== el) el.value = cart?.note || "";
|
|
1049
1143
|
});
|
|
@@ -1112,7 +1206,15 @@
|
|
|
1112
1206
|
qsa(node, sel("line")).forEach((el) => {
|
|
1113
1207
|
const key = get(el, "line");
|
|
1114
1208
|
if (key === "image") return setImage(el, m.image?.url || m.product?.featuredImage?.url, m.product?.title);
|
|
1115
|
-
if (key === "url")
|
|
1209
|
+
if (key === "url") {
|
|
1210
|
+
const u = productUrl(m.product?.handle, m.product?.id);
|
|
1211
|
+
const q = new URLSearchParams();
|
|
1212
|
+
if (m.id) q.set("variant", String(m.id).split("/").pop());
|
|
1213
|
+
const plan = line.sellingPlanAllocation?.sellingPlan?.id;
|
|
1214
|
+
if (plan) q.set("selling_plan", plan.split("/").pop());
|
|
1215
|
+
const qs = q.toString();
|
|
1216
|
+
return el.setAttribute("href", qs ? `${u}${u.includes("?") ? "&" : "?"}${qs}` : u);
|
|
1217
|
+
}
|
|
1116
1218
|
const v = lineValue(line, key);
|
|
1117
1219
|
if (v !== void 0) setTextTracked(el, v);
|
|
1118
1220
|
return void 0;
|
|
@@ -1158,6 +1260,7 @@
|
|
|
1158
1260
|
var product_exports = {};
|
|
1159
1261
|
__export(product_exports, {
|
|
1160
1262
|
ProductScope: () => ProductScope,
|
|
1263
|
+
rememberOptionNames: () => rememberOptionNames,
|
|
1161
1264
|
scopes: () => scopes
|
|
1162
1265
|
});
|
|
1163
1266
|
|
|
@@ -1204,11 +1307,7 @@
|
|
|
1204
1307
|
scoped(root, sel("show"), boundary()).forEach((el) => {
|
|
1205
1308
|
const k = get(el, "show");
|
|
1206
1309
|
if (!STATUS_KEYS.includes(k)) return;
|
|
1207
|
-
|
|
1208
|
-
show(el, visible);
|
|
1209
|
-
if (visible && typeof getComputedStyle === "function" && getComputedStyle(el).display === "none") {
|
|
1210
|
-
el.style.display = el.getAttribute(attr("display")) || "block";
|
|
1211
|
-
}
|
|
1310
|
+
show(el, k === status);
|
|
1212
1311
|
});
|
|
1213
1312
|
if (status === "error" || notFound) {
|
|
1214
1313
|
scoped(root, sel("add-to-cart"), boundary()).forEach((btn) => {
|
|
@@ -1229,6 +1328,12 @@
|
|
|
1229
1328
|
const mf = (product.metafields || []).find((m) => m && m.namespace === ns && m.key === key);
|
|
1230
1329
|
return mf || null;
|
|
1231
1330
|
}
|
|
1331
|
+
var knownOptionNames = /* @__PURE__ */ new Map();
|
|
1332
|
+
function rememberOptionNames(products) {
|
|
1333
|
+
(products || []).forEach((p) => {
|
|
1334
|
+
if (p?.handle && !knownOptionNames.has(p.handle)) knownOptionNames.set(p.handle, (p.options || []).map((o) => o.name));
|
|
1335
|
+
});
|
|
1336
|
+
}
|
|
1232
1337
|
var ProductScope = class {
|
|
1233
1338
|
constructor(root, product) {
|
|
1234
1339
|
this.root = root;
|
|
@@ -1278,6 +1383,13 @@
|
|
|
1278
1383
|
return this.root.matches(s) ? [this.root, ...inner] : inner;
|
|
1279
1384
|
}
|
|
1280
1385
|
// ------------------------------------------------------------ state
|
|
1386
|
+
/** The product's option name for a name used in the markup (same name, or same position). */
|
|
1387
|
+
optName(authored) {
|
|
1388
|
+
const opts = this.product?.options || [];
|
|
1389
|
+
if (!authored || opts.some((o) => o.name === authored)) return authored;
|
|
1390
|
+
const i = (knownOptionNames.get(this.product.handle) || []).indexOf(authored);
|
|
1391
|
+
return i >= 0 && opts[i] ? opts[i].name : authored;
|
|
1392
|
+
}
|
|
1281
1393
|
initSelection() {
|
|
1282
1394
|
const vs = this.product.variants.nodes;
|
|
1283
1395
|
const first = vs.find((v) => v.availableForSale) || vs[0];
|
|
@@ -1394,7 +1506,7 @@
|
|
|
1394
1506
|
const opt = t.closest(sel("option-value"));
|
|
1395
1507
|
if (own(opt) && opt.closest(sel("option-list"))) {
|
|
1396
1508
|
e.preventDefault();
|
|
1397
|
-
this.select(get(opt.closest(sel("option-list")), "option-list"), get(opt, "option-value"));
|
|
1509
|
+
this.select(this.optName(get(opt.closest(sel("option-list")), "option-list")), get(opt, "option-value"));
|
|
1398
1510
|
return;
|
|
1399
1511
|
}
|
|
1400
1512
|
const plan = t.closest(sel("plan-value"));
|
|
@@ -1430,7 +1542,7 @@
|
|
|
1430
1542
|
this.root.addEventListener("change", (e) => {
|
|
1431
1543
|
const t = e.target;
|
|
1432
1544
|
if (!own(t)) return;
|
|
1433
|
-
if (t.matches(`select${sel("option")}`)) this.select(get(t, "option"), t.value);
|
|
1545
|
+
if (t.matches(`select${sel("option")}`)) this.select(this.optName(get(t, "option")), t.value);
|
|
1434
1546
|
else if (t.matches(`select${sel("variant-select")}`)) this.selectVariant(t.value);
|
|
1435
1547
|
else if (t.matches(`select${sel("plan")}`)) {
|
|
1436
1548
|
if (t.value) this.selectPlan(t.value);
|
|
@@ -1460,6 +1572,11 @@
|
|
|
1460
1572
|
}
|
|
1461
1573
|
// ------------------------------------------------------------ render
|
|
1462
1574
|
renderProduct() {
|
|
1575
|
+
const p0 = this.product;
|
|
1576
|
+
if (!knownOptionNames.has(p0.handle)) {
|
|
1577
|
+
const authored = this.$(`${sel("option-list")},select${sel("option")}`).map((e) => get(e, "option-list") || get(e, "option"));
|
|
1578
|
+
if (authored.some((n) => p0.options.some((o) => o.name === n))) rememberOptionNames([p0]);
|
|
1579
|
+
}
|
|
1463
1580
|
const p = this.product;
|
|
1464
1581
|
this.$(sel("bind")).forEach((el) => {
|
|
1465
1582
|
const key = get(el, "bind") || "";
|
|
@@ -1489,6 +1606,10 @@
|
|
|
1489
1606
|
if (key === "url" && !el.firstElementChild && !el.textContent.trim()) setText(el, p.title);
|
|
1490
1607
|
return void 0;
|
|
1491
1608
|
case "images":
|
|
1609
|
+
{
|
|
1610
|
+
const imgs = [...el.children].map((n) => n.tagName === "IMG" ? n : n.querySelector("img"));
|
|
1611
|
+
if (!hasTemplate(el) && imgs.length === p.images.nodes.length && imgs.every((im, i) => sameImage(im?.getAttribute("src"), p.images.nodes[i].url))) return void 0;
|
|
1612
|
+
}
|
|
1492
1613
|
return repeat(el, p.images.nodes, (node, img) => {
|
|
1493
1614
|
const target = node.matches(`img,${sel("bind", "image")}`) ? node : node.querySelector("img") || node;
|
|
1494
1615
|
setImage(target, img.url, img.altText || p.title);
|
|
@@ -1513,7 +1634,7 @@
|
|
|
1513
1634
|
}
|
|
1514
1635
|
});
|
|
1515
1636
|
this.$(sel("option-list")).forEach((container) => {
|
|
1516
|
-
const name = get(container, "option-list");
|
|
1637
|
+
const name = this.optName(get(container, "option-list"));
|
|
1517
1638
|
const opt = p.options.find((o) => o.name === name);
|
|
1518
1639
|
if (!opt || opt.optionValues.length <= 1 && has(container, "hide-single")) return show(container, false);
|
|
1519
1640
|
return repeat(container, opt.optionValues, (node, v) => {
|
|
@@ -1527,7 +1648,7 @@
|
|
|
1527
1648
|
});
|
|
1528
1649
|
});
|
|
1529
1650
|
this.$(`select${sel("option")}`).forEach((s) => {
|
|
1530
|
-
const opt = p.options.find((o) => o.name === get(s, "option"));
|
|
1651
|
+
const opt = p.options.find((o) => o.name === this.optName(get(s, "option")));
|
|
1531
1652
|
if (!opt) return show(s, false);
|
|
1532
1653
|
s.innerHTML = "";
|
|
1533
1654
|
opt.optionValues.forEach((v) => s.appendChild(new Option(v.name, v.name)));
|
|
@@ -1611,9 +1732,9 @@
|
|
|
1611
1732
|
if (k === "variant-image") setImage(el, v.image?.url || p.featuredImage?.url, v.image?.altText || p.title);
|
|
1612
1733
|
else if (text[k]) setText(el, text[k]());
|
|
1613
1734
|
});
|
|
1614
|
-
this.$(sel("option-selected")).forEach((el) => setText(el, this.selected[get(el, "option-selected")] || ""));
|
|
1735
|
+
this.$(sel("option-selected")).forEach((el) => setText(el, this.selected[this.optName(get(el, "option-selected"))] || ""));
|
|
1615
1736
|
this.$(sel("option-list")).forEach((container) => {
|
|
1616
|
-
const name = get(container, "option-list");
|
|
1737
|
+
const name = this.optName(get(container, "option-list"));
|
|
1617
1738
|
qsa(container, sel("option-value")).forEach((node) => {
|
|
1618
1739
|
const value = get(node, "option-value");
|
|
1619
1740
|
setSelected(node, this.selected[name] === value);
|
|
@@ -1623,7 +1744,7 @@
|
|
|
1623
1744
|
});
|
|
1624
1745
|
});
|
|
1625
1746
|
this.$(`select${sel("option")}`).forEach((s) => {
|
|
1626
|
-
s.value = this.selected[get(s, "option")] || "";
|
|
1747
|
+
s.value = this.selected[this.optName(get(s, "option"))] || "";
|
|
1627
1748
|
});
|
|
1628
1749
|
this.$(`select${sel("variant-select")}`).forEach((s) => {
|
|
1629
1750
|
s.value = v.id;
|
|
@@ -1756,18 +1877,18 @@
|
|
|
1756
1877
|
}
|
|
1757
1878
|
async load(more = false) {
|
|
1758
1879
|
const token = this.loadToken = (this.loadToken || 0) + 1;
|
|
1759
|
-
const
|
|
1880
|
+
const stale2 = () => token !== this.loadToken;
|
|
1760
1881
|
try {
|
|
1761
1882
|
if (has(this.root, "recommendations")) {
|
|
1762
1883
|
const key = get(this.root, "recommendations");
|
|
1763
1884
|
const base = await getProduct(key);
|
|
1764
1885
|
const recs = await getRecommendations(base.id, get(this.root, "intent") || "RELATED");
|
|
1765
|
-
if (
|
|
1886
|
+
if (stale2()) return;
|
|
1766
1887
|
const limit = parseInt(get(this.root, "limit") || "", 10);
|
|
1767
1888
|
this.products = limit ? recs.slice(0, limit) : recs;
|
|
1768
1889
|
} else {
|
|
1769
1890
|
const res = await getProducts({ ...this.options(), after: more ? this.pageInfo?.endCursor : null });
|
|
1770
|
-
if (
|
|
1891
|
+
if (stale2()) return;
|
|
1771
1892
|
this.collection = res.collection;
|
|
1772
1893
|
this.pageInfo = res.pageInfo;
|
|
1773
1894
|
this.products = more ? [...this.products, ...res.products] : res.products;
|
|
@@ -1776,7 +1897,7 @@
|
|
|
1776
1897
|
setScopeStatus(this.root, "ready");
|
|
1777
1898
|
emit("list-ready", { products: this.products, collection: this.collection }, this.root);
|
|
1778
1899
|
} catch (err) {
|
|
1779
|
-
if (
|
|
1900
|
+
if (stale2()) return;
|
|
1780
1901
|
if (!more) setScopeStatus(this.root, isNotFound(err) ? "not-found" : "error", err);
|
|
1781
1902
|
reportError(err, "list load failed");
|
|
1782
1903
|
emit("error", { error: err, context: "list" }, this.root);
|
|
@@ -1822,6 +1943,7 @@
|
|
|
1822
1943
|
// src/mounting.js
|
|
1823
1944
|
var mounting_exports = {};
|
|
1824
1945
|
__export(mounting_exports, {
|
|
1946
|
+
hydrateFromPage: () => hydrateFromPage,
|
|
1825
1947
|
liveLists: () => liveLists,
|
|
1826
1948
|
liveProducts: () => liveProducts,
|
|
1827
1949
|
mountProduct: () => mountProduct,
|
|
@@ -1873,6 +1995,7 @@
|
|
|
1873
1995
|
if (existing) existing.update(p);
|
|
1874
1996
|
else new ProductScope(el, p);
|
|
1875
1997
|
applyPageTitle(el, p);
|
|
1998
|
+
if (cfg.revalidate !== false && takeStale(key)) queueRefresh(key);
|
|
1876
1999
|
}).catch((err) => {
|
|
1877
2000
|
if (!current()) return;
|
|
1878
2001
|
const notFound = isNotFound(err);
|
|
@@ -1901,6 +2024,40 @@
|
|
|
1901
2024
|
mountProduct(el);
|
|
1902
2025
|
});
|
|
1903
2026
|
}
|
|
2027
|
+
var refreshQueue = /* @__PURE__ */ new Set();
|
|
2028
|
+
function queueRefresh(key) {
|
|
2029
|
+
if (!refreshQueue.size) setTimeout(flushRefresh, 0);
|
|
2030
|
+
refreshQueue.add(key);
|
|
2031
|
+
}
|
|
2032
|
+
function flushRefresh() {
|
|
2033
|
+
const keys = [...refreshQueue];
|
|
2034
|
+
refreshQueue = /* @__PURE__ */ new Set();
|
|
2035
|
+
refreshProducts(keys).forEach((promise, i) => promise.then((fresh) => {
|
|
2036
|
+
liveProducts.forEach((el) => {
|
|
2037
|
+
const scope = scopes.get(el);
|
|
2038
|
+
if (!scope || !el.isConnected) return;
|
|
2039
|
+
const k = resolveProductKey(el).key;
|
|
2040
|
+
if (k !== keys[i] && k !== fresh.handle && k !== fresh.id) return;
|
|
2041
|
+
if (JSON.stringify(scope.product) !== JSON.stringify(fresh)) scope.update(fresh);
|
|
2042
|
+
});
|
|
2043
|
+
}).catch((err) => reportError(err, `refresh ${keys[i]}`)));
|
|
2044
|
+
}
|
|
2045
|
+
var readScripts = /* @__PURE__ */ new WeakSet();
|
|
2046
|
+
function hydrateFromPage(root = document) {
|
|
2047
|
+
qsa(root.ownerDocument || root, 'script[type="application/json"][data-tether-hydrate]').forEach((s) => {
|
|
2048
|
+
if (readScripts.has(s)) return;
|
|
2049
|
+
readScripts.add(s);
|
|
2050
|
+
try {
|
|
2051
|
+
const data = JSON.parse(s.textContent);
|
|
2052
|
+
rememberOptionNames(data.products);
|
|
2053
|
+
const same = (a, b) => String(a || "").toUpperCase() === String(b || "").toUpperCase();
|
|
2054
|
+
if (same(data.country, cfg.country) && same(data.language, cfg.language)) hydrateProducts(data.products);
|
|
2055
|
+
else log("hydration skipped: other market");
|
|
2056
|
+
} catch (err) {
|
|
2057
|
+
warn("bad data-tether-hydrate", err);
|
|
2058
|
+
}
|
|
2059
|
+
});
|
|
2060
|
+
}
|
|
1904
2061
|
|
|
1905
2062
|
// src/lazy-iife.js
|
|
1906
2063
|
var { cfg: cfg2 } = config_exports;
|
|
@@ -1939,13 +2096,14 @@
|
|
|
1939
2096
|
}
|
|
1940
2097
|
|
|
1941
2098
|
// src/index.js
|
|
1942
|
-
var VERSION = "0.
|
|
2099
|
+
var VERSION = "0.5.0";
|
|
1943
2100
|
var initPromise = null;
|
|
1944
2101
|
var observer = null;
|
|
1945
2102
|
function mount(root = document) {
|
|
1946
2103
|
if (!isConfigured()) return 0;
|
|
1947
2104
|
let count = 0;
|
|
1948
2105
|
pruneLive();
|
|
2106
|
+
hydrateFromPage(root);
|
|
1949
2107
|
within(root, listSelector()).forEach((el) => {
|
|
1950
2108
|
if (mountedLists.has(el)) return;
|
|
1951
2109
|
mountedLists.add(el);
|
|
@@ -2055,6 +2213,8 @@
|
|
|
2055
2213
|
try {
|
|
2056
2214
|
const cart = await Cart.applyDiscount(code);
|
|
2057
2215
|
const ok = (cart?.discountCodes || []).find((d) => d.code.toLowerCase() === code.toLowerCase())?.applicable;
|
|
2216
|
+
if (!ok) await Cart.removeDiscount(code).catch(() => {
|
|
2217
|
+
});
|
|
2058
2218
|
form.classList.toggle("is-invalid", !ok);
|
|
2059
2219
|
emit(ok ? "discount-applied" : "discount-invalid", { code }, form);
|
|
2060
2220
|
if (ok && input) input.value = "";
|
|
@@ -2063,7 +2223,14 @@
|
|
|
2063
2223
|
reportError(err, "discount");
|
|
2064
2224
|
}
|
|
2065
2225
|
});
|
|
2226
|
+
}, true);
|
|
2227
|
+
document.addEventListener("input", (e) => {
|
|
2228
|
+
e.target.closest?.(sel("discount-form"))?.classList.remove("is-invalid");
|
|
2066
2229
|
});
|
|
2230
|
+
if (cfg.astro !== false) {
|
|
2231
|
+
document.addEventListener("astro:page-load", () => mount(document));
|
|
2232
|
+
document.addEventListener("astro:before-swap", () => unmount());
|
|
2233
|
+
}
|
|
2067
2234
|
window.addEventListener("online", () => {
|
|
2068
2235
|
retryFailed();
|
|
2069
2236
|
liveLists.forEach((l) => {
|
|
@@ -2160,7 +2327,7 @@
|
|
|
2160
2327
|
if (!market) return;
|
|
2161
2328
|
if (market.country) cfg.country = market.country;
|
|
2162
2329
|
if (market.language) cfg.language = market.language;
|
|
2163
|
-
if (market.locale && !overrides.locale
|
|
2330
|
+
if (market.locale && !overrides.locale) cfg.locale = market.locale;
|
|
2164
2331
|
log("market", market);
|
|
2165
2332
|
}
|
|
2166
2333
|
function setCountry(code, opts) {
|
|
@@ -2207,6 +2374,7 @@
|
|
|
2207
2374
|
normalizeProductKey,
|
|
2208
2375
|
clearProductCache,
|
|
2209
2376
|
formatMoney,
|
|
2377
|
+
imageUrl,
|
|
2210
2378
|
plans: selling_plans_exports,
|
|
2211
2379
|
TetherError,
|
|
2212
2380
|
resolveProductKey,
|
|
@@ -2225,7 +2393,7 @@
|
|
|
2225
2393
|
var index_default = api;
|
|
2226
2394
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
2227
2395
|
window.Tether = api;
|
|
2228
|
-
const auto = window.TETHER_CONFIG && window.TETHER_CONFIG.autoMount !== false;
|
|
2396
|
+
const auto = window.TETHER_CONFIG && window.TETHER_CONFIG.autoMount !== false && !window.TETHER_INTEGRATION;
|
|
2229
2397
|
if (auto) {
|
|
2230
2398
|
const start = () => init().then(() => mount(document));
|
|
2231
2399
|
if (document.readyState === "loading") document.addEventListener("DOMContentLoaded", start, { once: true });
|