@occluze/tether 0.4.0 → 0.6.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 +12 -8
- package/dist/esm/astro.js +2 -0
- package/dist/esm/chunks/account-WAWNASZU.js +1 -0
- package/dist/esm/chunks/analytics-22NMIVBV.js +1 -0
- package/dist/esm/chunks/chunk-4UR45XTC.js +1 -0
- package/dist/esm/chunks/chunk-6R752OYZ.js +1 -0
- package/dist/esm/chunks/chunk-BGIKQY6H.js +1 -0
- package/dist/esm/chunks/chunk-CISGFYOF.js +1 -0
- package/dist/esm/chunks/chunk-KARKXC6G.js +1 -0
- package/dist/esm/chunks/chunk-VC46IEJQ.js +1 -0
- package/dist/esm/chunks/chunk-YXCXIPQV.js +4 -0
- package/dist/esm/chunks/{chunk-KYQ5OGBX.js → chunk-YZMBXS6Y.js} +1 -1
- package/dist/esm/chunks/chunk-ZVUPVSLF.js +1 -0
- package/dist/esm/chunks/diagnose-6ZSK5DGE.js +1 -0
- package/dist/esm/chunks/extras-QDCSW6VU.js +1 -0
- package/dist/esm/chunks/markets-ui-HJCVLCKJ.js +1 -0
- package/dist/esm/chunks/search-LN2H6SXS.js +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +1 -0
- package/dist/tether.account.js +374 -0
- package/dist/tether.account.min.js +1 -0
- package/dist/tether.analytics.js +259 -0
- package/dist/tether.analytics.min.js +1 -0
- package/dist/tether.diagnose.js +14 -4
- package/dist/tether.diagnose.min.js +1 -1
- package/dist/tether.extras.js +87 -0
- package/dist/tether.extras.min.js +1 -0
- package/dist/tether.js +486 -125
- package/dist/tether.markets.js +17 -11
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +3 -3
- package/dist/tether.min.js.map +3 -3
- package/dist/tether.search.js +176 -0
- package/dist/tether.search.min.js +1 -0
- package/package.json +31 -5
- package/src/account.js +313 -0
- package/src/analytics.js +232 -0
- package/src/astro.js +76 -0
- package/src/cart.js +119 -16
- package/src/client.js +30 -15
- package/src/collection.js +15 -10
- package/src/config.js +5 -0
- package/src/diagnose.js +12 -3
- package/src/dom.js +39 -10
- package/src/extras.js +63 -0
- package/src/fragments.js +13 -12
- package/src/index.js +114 -40
- package/src/initial.js +75 -0
- package/src/lazy.js +9 -1
- package/src/loader.js +53 -7
- package/src/markets-ui.js +15 -6
- package/src/markets.js +1 -1
- package/src/mounting.js +68 -3
- package/src/parts/account.js +3 -0
- package/src/parts/analytics.js +3 -0
- package/src/parts/extras.js +3 -0
- package/src/parts/search.js +3 -0
- package/src/product.js +51 -13
- package/src/search.js +153 -0
- package/src/server.js +251 -0
- package/src/status.js +3 -8
- package/types/astro.d.ts +15 -0
- package/types/index.d.ts +72 -4
- package/types/server.d.ts +102 -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/types/index.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ export interface ShopifyProduct {
|
|
|
51
51
|
variants: { nodes: ShopifyVariant[] };
|
|
52
52
|
sellingPlanGroups?: { nodes: unknown[] };
|
|
53
53
|
metafields?: ({ namespace: string; key: string; type: string; value: string; reference?: unknown } | null)[];
|
|
54
|
+
seo?: { title?: string | null; description?: string | null };
|
|
55
|
+
onlineStoreUrl?: string | null;
|
|
54
56
|
[k: string]: unknown;
|
|
55
57
|
}
|
|
56
58
|
|
|
@@ -60,7 +62,7 @@ export interface ShopifyCartLine {
|
|
|
60
62
|
merchandise: ShopifyVariant & { product: Pick<ShopifyProduct, 'id' | 'handle' | 'title'> & Record<string, unknown> };
|
|
61
63
|
sellingPlanAllocation?: { sellingPlan: { id: string; name: string } } | null;
|
|
62
64
|
attributes?: { key: string; value: string }[];
|
|
63
|
-
cost:
|
|
65
|
+
cost: { totalAmount: Money; subtotalAmount: Money; amountPerQuantity: Money; compareAtAmountPerQuantity?: Money | null };
|
|
64
66
|
[k: string]: unknown;
|
|
65
67
|
}
|
|
66
68
|
|
|
@@ -117,6 +119,39 @@ export interface TetherConfig {
|
|
|
117
119
|
sellingPlansFirst?: number;
|
|
118
120
|
collectionFirst?: number;
|
|
119
121
|
retries?: number;
|
|
122
|
+
/** Products handed over by the server (tether/server hydrate()): refresh once in the background (default true). */
|
|
123
|
+
revalidate?: boolean;
|
|
124
|
+
/** Seconds a Shopify answer may be cached (default 60). Shopify caches identical queries, prices included, for hours otherwise; 0 = Shopify's caching. */
|
|
125
|
+
maxAge?: number;
|
|
126
|
+
/** Analytics (optional part): GA4/GTM dataLayer events, Shopify's analytics (experimental), or your own handler. */
|
|
127
|
+
analytics?: {
|
|
128
|
+
dataLayer?: boolean | string;
|
|
129
|
+
shopify?: boolean | { consent?: 'no-banner' | 'default-banner' | 'custom-banner'; proxy?: string };
|
|
130
|
+
onEvent?: (name: string, data: Record<string, unknown>) => void;
|
|
131
|
+
} | null;
|
|
132
|
+
/** Customer accounts (optional part): the Headless channel's Customer Account API client id, and options. */
|
|
133
|
+
account?: {
|
|
134
|
+
clientId: string;
|
|
135
|
+
/** The page Shopify sends the visitor back to after login (registered as callback URI). Default: the current page. */
|
|
136
|
+
callbackUrl?: string;
|
|
137
|
+
/** Where Shopify sends the visitor after logout (registered as logout URI). Default: the home page. */
|
|
138
|
+
logoutUrl?: string;
|
|
139
|
+
/** Keep the login across tabs and browser restarts (localStorage instead of sessionStorage). */
|
|
140
|
+
remember?: boolean;
|
|
141
|
+
/** Your own token endpoint, if the browser can't call Shopify's (it forwards the form body). */
|
|
142
|
+
tokenUrl?: string;
|
|
143
|
+
/** The *.myshopify.com domain, if domain is something else. */
|
|
144
|
+
shopDomain?: string;
|
|
145
|
+
locale?: string;
|
|
146
|
+
} | null;
|
|
147
|
+
/** tether-show="low-stock" at this many or fewer (default 5). */
|
|
148
|
+
lowStock?: number;
|
|
149
|
+
/** true: ?variant= (and selling_plan=) follow the visitor's choice on the page's main product. */
|
|
150
|
+
variantUrl?: boolean;
|
|
151
|
+
/** Your text for Shopify's cart warnings (stock, codes), e.g. translated by code. Return nothing to keep Shopify's. */
|
|
152
|
+
warningText?: ((warning: CartWarning) => string | null | undefined) | null;
|
|
153
|
+
/** Mount on Astro View Transitions (astro:page-load) and close the cart on navigation (default true). */
|
|
154
|
+
astro?: boolean;
|
|
120
155
|
storageKey?: string | null;
|
|
121
156
|
cartDrawerOpenClass?: string;
|
|
122
157
|
openCartOnAdd?: boolean;
|
|
@@ -163,11 +198,18 @@ export interface LineInput {
|
|
|
163
198
|
attributes?: Attributes;
|
|
164
199
|
}
|
|
165
200
|
|
|
201
|
+
/** Shopify's note on the last cart change; target is the cart line id it's about. */
|
|
202
|
+
export interface CartWarning { code: string; message: string; target?: string }
|
|
203
|
+
|
|
166
204
|
export interface Cart {
|
|
167
205
|
readonly state: ShopifyCart | null;
|
|
168
206
|
readonly lines: ShopifyCartLine[];
|
|
169
207
|
readonly count: number;
|
|
170
208
|
lastError: Error | null;
|
|
209
|
+
/** Shopify's notes on the last change, e.g. less stock than asked (code MERCHANDISE_NOT_ENOUGH_STOCK). */
|
|
210
|
+
readonly warnings: CartWarning[];
|
|
211
|
+
/** true once the saved cart (or the absence of one) is known. */
|
|
212
|
+
readonly loaded: boolean;
|
|
171
213
|
id(): string | null;
|
|
172
214
|
init(): Promise<ShopifyCart | null>;
|
|
173
215
|
/** add(variantId, qty) · add({ variantId, quantity, sellingPlanId, attributes }) · add([line, line]) */
|
|
@@ -175,10 +217,15 @@ export interface Cart {
|
|
|
175
217
|
/** updateLine(id, 3) · updateLine(id, { sellingPlanId: null }) switches to one-time. */
|
|
176
218
|
updateLine(lineId: string, patch: number | (LineInput & { quantity?: number })): Promise<ShopifyCart>;
|
|
177
219
|
removeLine(lineIds: string | string[]): Promise<ShopifyCart>;
|
|
220
|
+
/** +1 / -1 for a line; quick clicks add up and show at once (use it for your own +/- buttons). */
|
|
221
|
+
changeQuantity(lineId: string, delta: number): Promise<ShopifyCart | null>;
|
|
222
|
+
/** The quantity to show for a line: what the visitor asked for while an update is on its way. */
|
|
223
|
+
quantityOf(line: ShopifyCartLine): number;
|
|
178
224
|
clear(): Promise<ShopifyCart | null>;
|
|
179
225
|
setNote(note: string): Promise<ShopifyCart>;
|
|
180
226
|
setAttributes(attributes: Attributes): Promise<ShopifyCart>;
|
|
181
227
|
setDiscountCodes(codes: string[]): Promise<ShopifyCart>;
|
|
228
|
+
/** Rejects with code INVALID_DISCOUNT (and doesn't keep the code) when Shopify doesn't accept it. */
|
|
182
229
|
applyDiscount(code: string): Promise<ShopifyCart>;
|
|
183
230
|
removeDiscount(code: string): Promise<ShopifyCart>;
|
|
184
231
|
setBuyerIdentity(identity: { countryCode?: string; email?: string; phone?: string }, opts?: { create?: boolean }): Promise<ShopifyCart | null>;
|
|
@@ -189,6 +236,8 @@ export interface Cart {
|
|
|
189
236
|
isOpen(): boolean;
|
|
190
237
|
drawers(): HTMLElement[];
|
|
191
238
|
render(): void;
|
|
239
|
+
/** For framework islands: fn(cart, meta) now, when the saved cart has loaded and after every change. Returns unsubscribe. */
|
|
240
|
+
subscribe(fn: (cart: ShopifyCart | null, meta: { open: boolean; count: number; loaded: boolean; warnings: CartWarning[] }) => void): () => void;
|
|
192
241
|
}
|
|
193
242
|
|
|
194
243
|
export interface ProductScope {
|
|
@@ -262,6 +311,7 @@ export interface TetherEvents {
|
|
|
262
311
|
'product-added': { product: ShopifyProduct; variant: ShopifyVariant; line: LineInput };
|
|
263
312
|
added: { cart: ShopifyCart; lines: LineInput[] };
|
|
264
313
|
'group-added': { group: string | null; lines: LineInput[] };
|
|
314
|
+
'cart-warning': { warnings: CartWarning[] };
|
|
265
315
|
'cart-updated': { cart: ShopifyCart | null; reason: string };
|
|
266
316
|
'cart-toggled': { open: boolean };
|
|
267
317
|
'cart-error': { error: Error };
|
|
@@ -290,18 +340,29 @@ export interface Tether {
|
|
|
290
340
|
cart: Cart;
|
|
291
341
|
getProduct(handleOrId: string): Promise<ShopifyProduct>;
|
|
292
342
|
getProducts(opts?: { collection?: string; query?: string; first?: number; after?: string | null; sortKey?: string; reverse?: boolean; filters?: unknown[] }): Promise<ProductPage>;
|
|
293
|
-
getRecommendations(
|
|
343
|
+
getRecommendations(product: string | Pick<ShopifyProduct, 'id'>, intent?: 'RELATED' | 'COMPLEMENTARY'): Promise<ShopifyProduct[]>;
|
|
294
344
|
getScope(el: Element | null | undefined): ProductScope | null;
|
|
295
345
|
addGroup(button: Element): Promise<ShopifyCart | null>;
|
|
346
|
+
/** Customer accounts (loads the account part): login(), logout(), customer(), orders(), query(gql, vars), getToken(). */
|
|
347
|
+
account: {
|
|
348
|
+
login(opts?: { returnTo?: string; locale?: string }): Promise<void>;
|
|
349
|
+
logout(opts?: { returnTo?: string }): Promise<void>;
|
|
350
|
+
customer(): Promise<{ id: string; firstName?: string | null; lastName?: string | null; displayName?: string; emailAddress?: { emailAddress: string } | null } | null>;
|
|
351
|
+
orders(opts?: { first?: number }): Promise<Array<{ id: string; name: string; processedAt: string; financialStatus?: string; fulfillmentStatus?: string; statusPageUrl: string; totalPrice: Money }>>;
|
|
352
|
+
query<T = any>(query: string, variables?: Record<string, unknown>): Promise<T>;
|
|
353
|
+
getToken(): Promise<string | null>;
|
|
354
|
+
};
|
|
296
355
|
primeProduct(product: ShopifyProduct): void;
|
|
297
356
|
normalizeProductKey(raw: string | number): string | null;
|
|
298
357
|
clearProductCache(): void;
|
|
299
358
|
formatMoney(money: Money | null | undefined): string;
|
|
359
|
+
/** Shopify CDN image at a size: imageUrl(line.merchandise.image?.url, { width: 160 }). */
|
|
360
|
+
imageUrl(url: string | null | undefined, size?: { width?: number; height?: number; crop?: string }): string;
|
|
300
361
|
plans: SellingPlanHelpers;
|
|
301
362
|
TetherError: typeof TetherError;
|
|
302
363
|
resolveProductKey(el: Element, location?: Location | URL): { key: string | null; fromUrl: boolean; raw: string };
|
|
303
364
|
/** Switch market: prices, currency, cart and every product on the page follow. */
|
|
304
|
-
setCountry(code: string, opts?:
|
|
365
|
+
setCountry(code: string, opts?: SetCountryOptions): Promise<ShopifyCart | null>;
|
|
305
366
|
detectMarket(markets?: MarketsConfig): Promise<{ country?: string; language?: string; locale?: string; sources: Record<string, string> }>;
|
|
306
367
|
getLocalization(): Promise<Localization>;
|
|
307
368
|
/** Logs and returns what's wrong on this page (config, token, API version, plans, markup). */
|
|
@@ -317,7 +378,14 @@ export declare function mount(root?: ParentNode): number;
|
|
|
317
378
|
export declare function unmount(): void;
|
|
318
379
|
export declare function getScope(el: Element | null | undefined): ProductScope | null;
|
|
319
380
|
export declare function addGroup(button: Element): Promise<ShopifyCart | null>;
|
|
320
|
-
export
|
|
381
|
+
export interface SetCountryOptions {
|
|
382
|
+
/** Shopify language (translations); null clears it. Default: the markets.locales entry for that country, else unchanged. */
|
|
383
|
+
language?: string | null;
|
|
384
|
+
/** Money format, e.g. 'de-DE'. Default: the markets.locales entry for that country, else unchanged. */
|
|
385
|
+
locale?: string;
|
|
386
|
+
remember?: boolean;
|
|
387
|
+
}
|
|
388
|
+
export declare function setCountry(code: string, opts?: SetCountryOptions): Promise<ShopifyCart | null>;
|
|
321
389
|
export declare const api: Tether;
|
|
322
390
|
declare const tether: Tether;
|
|
323
391
|
export default tether;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import type { ShopifyProduct, ShopifyVariant, Money, TetherError, SellingPlanHelpers, TetherConfig } from './index.js';
|
|
2
|
+
export type { ShopifyProduct, ShopifyVariant, ShopifyCart, ShopifyCartLine, Money } from './index.js';
|
|
3
|
+
|
|
4
|
+
export interface StorefrontOptions extends Partial<Pick<TetherConfig,
|
|
5
|
+
'apiVersion' | 'country' | 'language' | 'locale' | 'productPath' | 'maxAge' | 'lowStock' | 'defaultPurchase' | 'metafields' | 'imagesFirst' | 'variantsFirst' | 'sellingPlanGroupsFirst' | 'sellingPlansFirst' | 'retries'>> {
|
|
6
|
+
/** your-store.myshopify.com (or pass import.meta.env with PUBLIC_SHOPIFY_DOMAIN). */
|
|
7
|
+
domain?: string;
|
|
8
|
+
/** Public Storefront API token (or PUBLIC_SHOPIFY_STOREFRONT_TOKEN in import.meta.env). */
|
|
9
|
+
token?: string;
|
|
10
|
+
/** Your site's address, e.g. https://shop.example: product URLs in jsonLd() without passing url. */
|
|
11
|
+
siteUrl?: string;
|
|
12
|
+
/** Products per request for allProducts() (default 25). */
|
|
13
|
+
pageSize?: number;
|
|
14
|
+
/** Custom fetch (tests, caching). */
|
|
15
|
+
fetch?: typeof fetch;
|
|
16
|
+
PUBLIC_SHOPIFY_DOMAIN?: string;
|
|
17
|
+
PUBLIC_SHOPIFY_STOREFRONT_TOKEN?: string;
|
|
18
|
+
[env: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ShopifyCollection {
|
|
22
|
+
id: string; handle: string; title: string; description: string; descriptionHtml: string;
|
|
23
|
+
image?: { url: string; altText?: string | null } | null;
|
|
24
|
+
seo?: { title?: string | null; description?: string | null };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ProductQuery {
|
|
28
|
+
collection?: string;
|
|
29
|
+
query?: string;
|
|
30
|
+
first?: number;
|
|
31
|
+
after?: string | null;
|
|
32
|
+
sortKey?: string;
|
|
33
|
+
reverse?: boolean;
|
|
34
|
+
filters?: unknown[];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export interface Storefront {
|
|
38
|
+
readonly config: Required<Pick<StorefrontOptions, 'domain' | 'token'>> & StorefrontOptions;
|
|
39
|
+
query<T = any>(query: string, variables?: Record<string, unknown>, opts?: { context?: boolean }): Promise<T>;
|
|
40
|
+
/** By handle, numeric id, gid or product URL. null when it doesn't exist. */
|
|
41
|
+
getProduct(handleOrId: string | number): Promise<ShopifyProduct | null>;
|
|
42
|
+
getProducts(opts?: ProductQuery): Promise<{ collection: ShopifyCollection | null; products: ShopifyProduct[]; pageInfo: { hasNextPage: boolean; endCursor: string | null } }>;
|
|
43
|
+
/** Every product (of a collection or a search query), page by page. For getStaticPaths(). */
|
|
44
|
+
allProducts(opts?: Omit<ProductQuery, 'first' | 'after'>): Promise<ShopifyProduct[]>;
|
|
45
|
+
getCollection(handle: string, opts?: Omit<ProductQuery, 'collection'>): Promise<(ShopifyCollection & { products: ShopifyProduct[]; pageInfo: { hasNextPage: boolean; endCursor: string | null } }) | null>;
|
|
46
|
+
allCollections(): Promise<ShopifyCollection[]>;
|
|
47
|
+
/** By handle, id or product (runs in parallel with getProduct when you pass the handle). */
|
|
48
|
+
getRecommendations(product: string | number | Pick<ShopifyProduct, 'id'>, intent?: 'RELATED' | 'COMPLEMENTARY'): Promise<ShopifyProduct[]>;
|
|
49
|
+
/**
|
|
50
|
+
* A <script type="application/json" data-tether-hydrate> tag: the browser script renders these
|
|
51
|
+
* products without its own request, then refreshes prices and stock once in the background.
|
|
52
|
+
* Astro: <Fragment set:html={shop.hydrate(product)} />
|
|
53
|
+
*/
|
|
54
|
+
/** { card: true } for lists: much smaller (no descriptions, one image), the rest comes with the refresh. */
|
|
55
|
+
hydrate(products: ShopifyProduct | ShopifyProduct[], opts?: { card?: boolean }): string;
|
|
56
|
+
formatMoney(money: Money | null | undefined, locale?: string): string;
|
|
57
|
+
/** 30 -> '30%' (nl, en) / '30 %' (de, fr), the way Tether shows savings. '' for 0. */
|
|
58
|
+
formatPercent(n: number, locale?: string): string;
|
|
59
|
+
productUrl(handle: string, opts?: { productPath?: string; id?: string }): string;
|
|
60
|
+
/** Shopify CDN image at a size. Use the element's max-width value, so the browser keeps the image. */
|
|
61
|
+
imageUrl(url: string | null | undefined, size?: ImageSize): string;
|
|
62
|
+
/**
|
|
63
|
+
* What Tether shows first for this product (variant, price, purchase mode, tether-show conditions),
|
|
64
|
+
* so the server renders the same and nothing flashes. Pass Astro.url.searchParams.get('variant') in SSR.
|
|
65
|
+
*/
|
|
66
|
+
initialState(product: ShopifyProduct, opts?: InitialOptions): InitialState | null;
|
|
67
|
+
/** schema.org Product / ProductGroup data for search engines, as a <script type="application/ld+json"> tag. */
|
|
68
|
+
jsonLd(product: ShopifyProduct | null, opts?: { url?: string; brand?: string }): string;
|
|
69
|
+
jsonLdData(product: ShopifyProduct | null, opts?: { url?: string; brand?: string }): Record<string, unknown> | null;
|
|
70
|
+
plans: SellingPlanHelpers;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface InitialState {
|
|
74
|
+
variant: ShopifyVariant;
|
|
75
|
+
/** option name -> value of that variant: is-selected on those buttons. */
|
|
76
|
+
selected: Record<string, string>;
|
|
77
|
+
purchase: 'one-time' | 'subscription';
|
|
78
|
+
plan: { id: string; name: string } | null;
|
|
79
|
+
plans: { id: string; name: string; price: Money; savings: number; selected: boolean; available: boolean }[];
|
|
80
|
+
savings: number;
|
|
81
|
+
/** The price Tether shows first (the plan price for products that require a subscription). */
|
|
82
|
+
price: Money;
|
|
83
|
+
/** Set only when on sale. */
|
|
84
|
+
compareAtPrice: Money | null;
|
|
85
|
+
available: boolean;
|
|
86
|
+
/** tether-show conditions: hidden={!state.show['out-of-stock']}. */
|
|
87
|
+
show: Record<'in-stock' | 'out-of-stock' | 'backorder' | 'on-sale' | 'not-on-sale' | 'has-variants' | 'single-variant' | 'low-stock' | 'subscription-available' | 'subscription-unavailable' | 'subscription-only' | 'one-time-available' | 'subscription-selected' | 'one-time-selected', boolean>;
|
|
88
|
+
/** true for option values without an available variant: class is-unavailable. */
|
|
89
|
+
isUnavailable(optionName: string, value: string): boolean;
|
|
90
|
+
}
|
|
91
|
+
export interface InitialOptions { variant?: string | null; sellingPlan?: string | null; defaultPurchase?: 'one-time' | 'subscription' }
|
|
92
|
+
export declare function initialState(product: ShopifyProduct, opts?: InitialOptions): InitialState | null;
|
|
93
|
+
|
|
94
|
+
export interface ImageSize { width?: number; height?: number; crop?: 'center' | 'top' | 'bottom' | 'left' | 'right' }
|
|
95
|
+
export declare function imageUrl(url: string | null | undefined, size?: ImageSize): string;
|
|
96
|
+
export declare function createStorefront(options?: StorefrontOptions): Storefront;
|
|
97
|
+
export declare function formatMoney(money: Money | null | undefined, locale?: string): string;
|
|
98
|
+
export declare function productUrl(handle: string, opts?: { productPath?: string; id?: string }): string;
|
|
99
|
+
export declare function productKey(raw: string | number): string | null;
|
|
100
|
+
export declare function withContext(query: string, ctx?: { country?: string; language?: string }): string;
|
|
101
|
+
export declare const plans: SellingPlanHelpers;
|
|
102
|
+
export { TetherError };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{B as f,C as o,D as l,E as T,F as H,G as m,H as K,I as g,J as $,K as vt,L as A,M as h,N as V,O as q,P as R,R as C,S as yt,T as _,U as wt,V as W,W as It,X as U,Y as At,Z as $t,_ as G,a as p,aa as Et,ba as k,d as Q,e as pt,f as I,g as et,j as M,k as it,l as st,o as F,q as ft,r as mt,s as x,t as N,u as L,v as nt,w as gt,x as at,y as bt,z as j}from"./chunk-LI3YDMGF.js";function Tt(s,t=1,e={}){let i=typeof s=="object"&&s!==null?s:{variantId:s,quantity:t,...e},n=x.variant(i.merchandiseId||i.variantId||i.id);if(!n)throw new et("add(): variantId is required");let a={merchandiseId:n,quantity:Math.max(1,parseInt(i.quantity??1,10)||1)},r=i.sellingPlanId||i.sellingPlan;r&&(a.sellingPlanId=x.sellingPlan(r));let u=U(i.attributes);return u.length&&(a.attributes=u),a}function Ct(s,t,e){return`mutation Tether_${s}(${t}){${s}(${e}){cart{...TetherCart}userErrors{field message}}}${st()}`}var _t='a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])',Y=null,Ot=s=>m(s,_t).filter(t=>t.offsetParent!==null||t===document.activeElement);function rt(s){m(document,o("cart-open")).forEach(t=>t.setAttribute("aria-expanded",String(s))),m(document,o("cart-drawer")).forEach(t=>{t.hasAttribute("role")||t.setAttribute("role","dialog"),!t.hasAttribute("aria-label")&&!t.hasAttribute("aria-labelledby")&&t.setAttribute("aria-label",l(t,"cart-label")||"Cart"),t.setAttribute("aria-modal",String(s)),t.classList.contains(p.cartDrawerOpenClass)||t.setAttribute("aria-hidden","true")})}var ot=s=>/does not exist|not found|invalid id/i.test(s?.message||""),Pt={state:null,_chain:Promise.resolve(),id(){try{return localStorage.getItem(p.storageKey)}catch{return null}},_setId(s){try{s?localStorage.setItem(p.storageKey,s):localStorage.removeItem(p.storageKey)}catch{}},get lines(){return this.state?.lines?.nodes||[]},get count(){return this.state?.totalQuantity||0},lastError:null,_queue(s){let t=this._chain.then(s,s).then(e=>(this.lastError&&(this.lastError=null,this.render()),e),e=>{throw this.lastError=e,this.render(),g("cart-error",{error:e}),e});return this._chain=t.catch(()=>{}),t},_commit(s,t){return this.state=s,s?.id&&this._setId(s.id),this.render(),g("cart-updated",{cart:s,reason:t}),s},async init(){let s=this.id();if(!s)return this.state=null,this.render(),null;try{let t=await M(`query TetherCart($id:ID!){cart(id:$id){...TetherCart}}${st()}`,{id:s});t.cart?this.state=t.cart:(this._setId(null),this.state=null)}catch(t){I(t,"cart load failed"),this.lastError=t,ot(t)&&this._setId(null),this.state=null}return this.render(),this.state},async _create(s={}){let t=p.country?{countryCode:String(p.country).toUpperCase()}:void 0,e=await M(Ct("cartCreate","$input: CartInput!","input: $input"),{input:{buyerIdentity:t,...s}});return it(e.cartCreate,"cartCreate").cart},async _mutate(s,t,e,i,n){let a=this.id();if(!a)return n?this._create(n):null;try{let r=await M(Ct(s,`$cartId: ID!, ${t}`,`cartId: $cartId, ${e}`),{...i,cartId:a});return it(r[s],s).cart}catch(r){if(n&&ot(r))return Q("cart expired, creating a new one"),this._setId(null),this._create(n);throw r}},add(s,t=1,e={}){let i=(Array.isArray(s)?s:[s]).map(n=>Tt(n,t,e));return this._queue(async()=>{let n=await this._mutate("cartLinesAdd","$lines: [CartLineInput!]!","lines: $lines",{lines:i},{lines:i});return this._commit(n,"add"),g("added",{cart:n,lines:i}),p.openCartOnAdd&&this.open(),n})},updateLine(s,t){let e=typeof t=="number"?{quantity:t}:{...t};if(e.quantity!==void 0&&e.quantity<=0)return this.removeLine(s);let i={id:s};return e.quantity!==void 0&&(i.quantity=e.quantity),"sellingPlanId"in e&&(i.sellingPlanId=e.sellingPlanId?x.sellingPlan(e.sellingPlanId):null),e.attributes&&(i.attributes=U(e.attributes)),(e.merchandiseId||e.variantId)&&(i.merchandiseId=x.variant(e.merchandiseId||e.variantId)),this._queue(async()=>this._commit(await this._mutate("cartLinesUpdate","$lines: [CartLineUpdateInput!]!","lines: $lines",{lines:[i]}),"update"))},removeLine(s){let t=Array.isArray(s)?s:[s];return this._queue(async()=>this._commit(await this._mutate("cartLinesRemove","$lineIds: [ID!]!","lineIds: $lineIds",{lineIds:t}),"remove"))},clear(){let s=this.lines.map(t=>t.id);return s.length?this.removeLine(s):Promise.resolve(this.state)},setNote(s){return this._queue(async()=>this._commit(await this._mutate("cartNoteUpdate","$note: String!","note: $note",{note:s??""},{note:s??""}),"note"))},setAttributes(s){let t=new Map((this.state?.attributes||[]).map(i=>[i.key,i.value]));U(s).forEach(i=>t.set(i.key,i.value));let e=[...t].filter(([,i])=>i!=="").map(([i,n])=>({key:i,value:n}));return this._queue(async()=>this._commit(await this._mutate("cartAttributesUpdate","$attributes: [AttributeInput!]!","attributes: $attributes",{attributes:e},{attributes:e}),"attributes"))},setDiscountCodes(s){let t=(Array.isArray(s)?s:[s]).map(e=>String(e).trim()).filter(Boolean);return this._queue(async()=>this._commit(await this._mutate("cartDiscountCodesUpdate","$discountCodes: [String!]!","discountCodes: $discountCodes",{discountCodes:t},{discountCodes:t}),"discount"))},applyDiscount(s){let t=(this.state?.discountCodes||[]).map(e=>e.code);return this.setDiscountCodes([...new Set([...t,s])])},removeDiscount(s){return this.setDiscountCodes((this.state?.discountCodes||[]).map(t=>t.code).filter(t=>t!==s))},setBuyerIdentity(s,{create:t=!0}={}){return this._queue(async()=>{let e=await this._mutate("cartBuyerIdentityUpdate","$buyerIdentity: CartBuyerIdentityInput!","buyerIdentity: $buyerIdentity",{buyerIdentity:s},t?{buyerIdentity:s}:null).catch(i=>{if(t||!ot(i))throw i;return this._setId(null),null});return this._commit(e,"buyer-identity")})},async checkout(s=p.checkoutTarget){let t=this.id();if(!t)return null;let e=s==="_blank"?window.open("","_blank"):null;if(e)try{e.opener=null}catch{}try{let n=(await M("query TetherCheckout($id: ID!) { cart(id: $id) { checkoutUrl } }",{id:t})).cart?.checkoutUrl;if(!n)throw new et("No checkoutUrl (cart empty or expired)");return g("checkout",{url:n,cart:this.state}),e&&!e.closed?e.location.href=n:window.location.href=n,n}catch(i){return e&&e.close(),I(i,"checkout failed"),null}},drawers(){return m(document,o("cart-drawer"))},isOpen(){return this.drawers().some(s=>s.classList.contains(p.cartDrawerOpenClass))},open(){if(this.isOpen())return;let s=this.drawers().filter(t=>l(t,"cart-drawer")!=="manual");s.forEach(t=>{t.classList.add(p.cartDrawerOpenClass),t.setAttribute("aria-hidden","false")}),rt(!0),p.cartFocus!==!1&&s[0]&&(Y=document.activeElement,setTimeout(()=>{let t=Ot(s[0])[0]||s[0];t===s[0]&&!s[0].hasAttribute("tabindex")&&(s[0].tabIndex=-1),t.focus?.({preventScroll:!0})},60)),typeof p.onCartOpen=="function"&&p.onCartOpen(s),g("cart-toggled",{open:!0})},close(){if(!this.isOpen())return;let s=this.drawers(),t=s.some(e=>e.contains(document.activeElement));s.forEach(e=>{e.classList.remove(p.cartDrawerOpenClass),e.setAttribute("aria-hidden","true")}),rt(!1),t&&Y?.isConnected&&Y.focus({preventScroll:!0}),Y=null,typeof p.onCartClose=="function"&&p.onCartClose(s),g("cart-toggled",{open:!1})},toggle(){this.isOpen()?this.close():this.open()},render(){let s=this.state,t=s?s.totalQuantity:0,e=a=>s?$(s.cost?.[a]):"";rt(this.isOpen()),m(document,o("cart-count")).forEach(a=>V(a,t)),m(document,o("cart-subtotal")).forEach(a=>V(a,e("subtotalAmount"))),m(document,o("cart-total")).forEach(a=>V(a,e("totalAmount"))),m(document,o("cart-checkout-charge")).forEach(a=>V(a,e("checkoutChargeAmount")));let i=this.lines.some(a=>a.sellingPlanAllocation),n={"cart-empty":t===0,"cart-filled":t>0,"cart-has-subscription":i,"cart-no-subscription":!i,"cart-has-discount":(s?.discountCodes||[]).some(a=>a.applicable),"cart-error":!!this.lastError};m(document,o("show")).forEach(a=>{let r=l(a,"show");r in n&&!a.closest(o("product"))&&q(a,n[r])}),m(document,`textarea${o("cart-note")},input${o("cart-note")}`).forEach(a=>{document.activeElement!==a&&(a.value=s?.note||"")}),m(document,o("cart-discounts")).forEach(a=>{C(a,s?.discountCodes||[],(r,u)=>{r.setAttribute(f("discount-code"),u.code);let b=r.querySelector(o("discount","code"))||(r.firstElementChild?null:r);b&&h(b,u.code),r.classList.toggle("is-inapplicable",!u.applicable)})}),m(document,o("cart-lines")).forEach(a=>{yt(a,this.lines,r=>r.id,(r,u)=>Ft(r,u),{onEnter:p.onLineEnter,onLeave:p.onLineLeave})}),s&&_("cart")}};function Dt(s,t){let e=s.merchandise||{},i=s.sellingPlanAllocation?.sellingPlan;switch(t){case"title":return e.product?.title;case"product-title":return e.product?.title;case"variant-title":return e.title==="Default Title"?"":e.title;case"options":return(e.selectedOptions||[]).filter(n=>n.value!=="Default Title").map(n=>`${n.name}: ${n.value}`).join(", ");case"vendor":return e.product?.vendor;case"sku":return e.sku;case"price":return $(s.cost?.amountPerQuantity||e.price);case"compare-at-price":return $(s.cost?.compareAtAmountPerQuantity||e.compareAtPrice);case"line-total":return $(s.cost?.totalAmount);case"line-subtotal":return $(s.cost?.subtotalAmount);case"quantity":return s.quantity;case"plan":return i?.name||"";case"plan-description":return i?.description||"";case"components":return(s.lineComponents||[]).map(n=>`${n.quantity} \xD7 ${n.merchandise?.product?.title||""}`).join(", ");default:if(t.startsWith("attribute:")){let n=t.slice(10);return(s.attributes||[]).find(a=>a.key===n)?.value||""}return}}function Ft(s,t){let e=t.merchandise||{},i=!!t.sellingPlanAllocation;s.setAttribute(f("line-id"),t.id),s.classList.toggle("is-subscription",i),m(s,o("line")).forEach(a=>{let r=l(a,"line");if(r==="image")return A(a,e.image?.url||e.product?.featuredImage?.url,e.product?.title);if(r==="url")return a.setAttribute("href",H(e.product?.handle,e.product?.id));let u=Dt(t,r);u!==void 0&&V(a,u)});let n={subscription:i,"one-time":!i,"on-sale":parseFloat(t.cost?.compareAtAmountPerQuantity?.amount)>parseFloat(t.cost?.amountPerQuantity?.amount),"has-components":(t.lineComponents||[]).length>0};m(s,o("line-show")).forEach(a=>{let r=l(a,"line-show");r in n&&q(a,n[r])}),m(s,`select${o("line-plan")}`).forEach(a=>{let r=e.product?.handle;r&&F(r).then(u=>{let b=N(u),O=[...!u.requiresSellingPlan&&l(a,"line-plan")!=="subscription-only"?[{value:"",label:a.getAttribute(f("one-time-label"))||"One-time purchase"}]:[],...b.map(E=>({value:E.id,label:E.name}))],D=O.map(E=>E.value).join("|");a.dataset.tetherSig!==D&&(a.innerHTML="",O.forEach(E=>{let v=document.createElement("option");v.value=E.value,v.textContent=E.label,a.appendChild(v)}),a.dataset.tetherSig=D),a.value=t.sellingPlanAllocation?.sellingPlan?.id||"",q(a,b.length>0)}).catch(u=>I(u,"line plan options"))})}var z=new WeakMap,St=()=>o("product");function Vt(s,t){let e=t.indexOf("."),i=t.slice(0,e),n=t.slice(e+1);return(s.metafields||[]).find(r=>r&&r.namespace===i&&r.key===n)||null}var J=class{constructor(t,e){this.root=t,this.product=e,this.selected={},this.variant=null,this.quantity=Math.max(0,parseInt(l(t,"quantity")??"1",10)||(T(t,"quantity")?0:1)),this.plans=N(e),this.purchase="one-time",this.planId=null,z.set(t,this),t.setAttribute(f("state"),"ready"),t.removeAttribute(f("error")),this.initSelection(),this.initPurchase(),this.renderProduct(),this.bind(),this.renderVariant(),wt(t,"product"),_("product"),g("product-ready",{product:e,scope:this},t)}update(t){let e=this.variant?.id;this.product=t,this.plans=N(t),this.planId&&!this.plans.some(n=>n.id===this.planId)&&(this.planId=this.plans[0]?.id||null),!this.plans.length&&this.purchase==="subscription"&&(this.purchase="one-time");let i=t.variants.nodes.find(n=>n.id===e);i&&(this.selected={},i.selectedOptions.forEach(n=>{this.selected[n.name]=n.value})),this.root.setAttribute(f("state"),"ready"),this.root.removeAttribute(f("error")),this.renderProduct(),this.renderVariant(),_("product"),g("product-updated",{product:t,scope:this},this.root)}$(t){let e=K(this.root,t,St());return this.root.matches(t)?[this.root,...e]:e}initSelection(){let t=this.product.variants.nodes,e=t.find(n=>n.availableForSale)||t[0];e&&e.selectedOptions.forEach(n=>{this.selected[n.name]=n.value});let i=new URLSearchParams(window.location.search).get("variant")||l(this.root,"variant");if(i){let n=x.variant(i),a=t.find(r=>r.id===n);a&&(this.selected={},a.selectedOptions.forEach(r=>{this.selected[r.name]=r.value}))}}initPurchase(){if(!this.plans.length)return;let t=new URLSearchParams(window.location.search),e=j(this.product,t.get("selling_plan")),i=j(this.product,l(this.root,"default-plan"));this.planId=(e||i||this.plans[0]).id;let n=e?"subscription":l(this.root,"default-purchase")||p.defaultPurchase;this.purchase=this.product.requiresSellingPlan||n==="subscription"?"subscription":"one-time"}findVariant(){return this.product.variants.nodes.find(t=>t.selectedOptions.every(e=>this.selected[e.name]===e.value))||null}isValueAvailable(t,e){return this.product.variants.nodes.some(i=>i.availableForSale&&i.selectedOptions.every(n=>n.name===t?n.value===e:this.selected[n.name]===n.value))}get plan(){return this.plans.find(t=>t.id===this.planId)||null}get subscriptionAvailable(){return!!(this.variant&&this.plans.some(t=>L(this.variant,t.id)))}get isSubscription(){return this.purchase==="subscription"&&!!L(this.variant,this.planId)}getLine(){if(!this.variant)return null;let t={merchandiseId:this.variant.id,quantity:this.quantity};this.isSubscription&&(t.sellingPlanId=this.planId);let e=this.collectAttributes();return e.length&&(t.attributes=e),t}collectAttributes(){let t=U(It(this.root,"line-attributes")),e=this.$(o("line-attribute")).map(i=>{let n=l(i,"line-attribute"),a;return i.type==="checkbox"?a=i.checked?i.value&&i.value!=="on"?i.value:"Yes":"":i.type==="radio"?a=i.checked?i.value:null:a=i.value,a==null||a===""?null:{key:n,value:String(a)}}).filter(Boolean);return[...t,...e]}select(t,e){this.selected[t]!==e&&(this.selected[t]=e,this.renderVariant())}selectVariant(t){let e=this.product.variants.nodes.find(i=>i.id===x.variant(t));e&&(this.selected={},e.selectedOptions.forEach(i=>{this.selected[i.name]=i.value}),this.renderVariant())}setPurchase(t){let e=t==="subscription"?"subscription":"one-time";e==="one-time"&&this.product.requiresSellingPlan||this.purchase!==e&&(this.purchase=e,this.renderVariant())}selectPlan(t){let e=j(this.product,t);e&&(this.planId=e.id,this.purchase="subscription",this.renderVariant())}setQuantity(t){let e=T(this.root,"quantity")?0:1;this.quantity=Math.max(e,parseInt(t,10)||0),this.$(o("qty")).forEach(i=>h(i,this.quantity)),this.renderVariant()}async addToCart(){if(!this.variant?.availableForSale)return null;let t=this.getLine();if(!t||t.quantity<1)return null;let e=await Pt.add(t);return g("product-added",{product:this.product,variant:this.variant,line:t},this.root),e}bind(){let t=e=>e&&e.closest(St())===this.root;this.root.addEventListener("click",e=>{let i=e.target;if(!i?.closest)return;let n=i.closest(o("option-value"));if(t(n)&&n.closest(o("option-list"))){e.preventDefault(),this.select(l(n.closest(o("option-list")),"option-list"),l(n,"option-value"));return}let a=i.closest(o("plan-value"));if(t(a)){e.preventDefault(),this.selectPlan(l(a,"plan-value"));return}let r=i.closest(`${o("purchase")}:not(select)`);if(t(r)){r.tagName!=="INPUT"&&e.preventDefault(),this.setPurchase(l(r,"purchase"));return}let u=i.closest(o("qty-plus"));if(t(u)){e.preventDefault(),this.setQuantity(this.quantity+1);return}let b=i.closest(o("qty-minus"));if(t(b)){e.preventDefault(),this.setQuantity(this.quantity-1);return}let B=i.closest(o("add-to-cart"));t(B)&&(e.preventDefault(),this.onAddClick(B))}),this.root.addEventListener("change",e=>{let i=e.target;t(i)&&(i.matches(`select${o("option")}`)?this.select(l(i,"option"),i.value):i.matches(`select${o("variant-select")}`)?this.selectVariant(i.value):i.matches(`select${o("plan")}`)?i.value?this.selectPlan(i.value):this.setPurchase("one-time"):i.matches(`select${o("purchase")}`)?this.setPurchase(i.value):i.matches(o("qty"))&&this.setQuantity(i.value))})}onAddClick(t){let e=t.querySelector(o("add-label"))||t,i=l(t,"adding-text"),n=a=>h(e,a);e.dataset.tetherLabel===void 0&&(e.dataset.tetherLabel=e.tagName==="INPUT"?e.value:e.textContent),W(t,async()=>{i&&n(i);try{await this.addToCart()}catch(a){I(a,"add to cart failed"),g("error",{error:a,context:"add-to-cart"},this.root)}finally{i&&n(e.dataset.tetherLabel)}}).then(()=>{this.renderVariant()})}renderProduct(){let t=this.product;this.$(o("bind")).forEach(e=>{let i=l(e,"bind")||"",n={title:t.title,"description-text":t.description,vendor:t.vendor,type:t.productType,handle:t.handle}[i];if(n!==void 0)return h(e,n);switch(i){case"description":e.innerHTML=t.descriptionHtml||"";return;case"published-at":return h(e,vt(t.publishedAt));case"min-price":return h(e,$(t.priceRange.minVariantPrice));case"max-price":return h(e,$(t.priceRange.maxVariantPrice));case"featured-image":return A(e,t.featuredImage?.url,t.featuredImage?.altText||t.title);case"url":case"link":e.setAttribute("href",H(t.handle,t.id)),i==="url"&&!e.firstElementChild&&!e.textContent.trim()&&h(e,t.title);return;case"images":return C(e,t.images.nodes,(a,r)=>{let u=a.matches(`img,${o("bind","image")}`)?a:a.querySelector("img")||a;A(u,r.url,r.altText||t.title)});case"tags":{let a=(e.getAttribute("exclude")||"").split(",").map(r=>r.trim()).filter(Boolean);return C(e,t.tags.filter(r=>!a.includes(r)),(r,u)=>h(r,u))}default:if(/^image:\d+$/.test(i)){let a=t.images.nodes[i.slice(6)-1]||t.featuredImage;return A(e,a?.url,a?.altText||t.title)}if(i.startsWith("metafield:")){let a=Vt(t,i.slice(10));return a?a.reference?.image?A(e,a.reference.image.url,a.reference.image.altText):(/^(rich_text|multi_line_text)_field$/.test(a.type)&&(e.style.whiteSpace="pre-line"),h(e,a.value)):h(e,"")}return}}),this.$(o("option-list")).forEach(e=>{let i=l(e,"option-list"),n=t.options.find(a=>a.name===i);return!n||n.optionValues.length<=1&&T(e,"hide-single")?q(e,!1):C(e,n.optionValues,(a,r)=>{a.setAttribute(f("option-value"),r.name);let u=a.querySelector(o("bind","option-value"));u?h(u,r.name):a.firstElementChild||h(a,r.name);let b=a.querySelector(o("bind","swatch"));b&&r.swatch?.color&&(b.style.backgroundColor=r.swatch.color),b&&r.swatch?.image?.previewImage&&A(b,r.swatch.image.previewImage.url,r.name)})}),this.$(`select${o("option")}`).forEach(e=>{let i=t.options.find(n=>n.name===l(e,"option"));if(!i)return q(e,!1);e.innerHTML="",i.optionValues.forEach(n=>e.appendChild(new Option(n.name,n.name)))}),this.$(`select${o("variant-select")}`).forEach(e=>{e.innerHTML="",t.variants.nodes.forEach(i=>e.appendChild(new Option(i.title,i.id)))}),this.$(o("plan-list")).forEach(e=>{let i=(l(e,"plan-list")||"").trim(),n=["","true","all","*"].includes(i.toLowerCase())?null:i,a=this.plans.filter(r=>!n||r.group.name===n);C(e,a,(r,u)=>{r.setAttribute(f("plan-value"),u.id),!r.firstElementChild&&!r.querySelector(o("plan-bind"))&&h(r,u.name)})}),this.$(`select${o("plan")}`).forEach(e=>{e.innerHTML="",!t.requiresSellingPlan&&l(e,"plan")!=="subscription-only"&&e.appendChild(new Option(e.getAttribute(f("one-time-label"))||"One-time purchase","")),this.plans.forEach(i=>e.appendChild(new Option(i.name,i.id)))})}renderVariant(){let t=this.product,e=this.findVariant();if(e||([e]=t.variants.nodes,e&&(this.selected={},e.selectedOptions.forEach(c=>{this.selected[c.name]=c.value}))),this.variant=e,!e)return;if(this.plans.length&&!L(e,this.planId)){let c=this.plans.find(d=>L(e,d.id));c&&(this.planId=c.id)}let i=this.isSubscription,n=e.price,a=this.planId?nt(e,this.planId):null,r=i?a:n,u=i&&L(e,this.planId)?.priceAdjustments?.[0]?.compareAtPrice||e.compareAtPrice,b=u&&parseFloat(u.amount)>parseFloat(r.amount),B=b?Math.round((1-parseFloat(r.amount)/parseFloat(u.amount))*100):0,O=this.planId?at(e,this.planId):{amount:null,percentage:0},D=this.plan,E={amount:(parseFloat(r.amount)*Math.max(1,this.quantity)).toFixed(2),currencyCode:r.currencyCode},v=$,ct=c=>c?`${c}%`:"",lt={price:()=>v(r),"total-price":()=>v(E),"one-time-price":()=>v(n),"subscription-price":()=>v(a),"subscription-savings":()=>ct(O.percentage),"subscription-savings-amount":()=>v(O.amount),"checkout-charge":()=>v(i?gt(e,this.planId):r),"plan-name":()=>D?.name,"plan-description":()=>D?.description,"plan-group":()=>D?.group?.name,"compare-at-price":()=>b?v(u):"","unit-price":()=>v(e.unitPrice),"discount-percentage":()=>`${B}%`,"discount-amount":()=>b?v({amount:String(parseFloat(u.amount)-parseFloat(r.amount)),currencyCode:r.currencyCode}):"","variant-title":()=>e.title,"variant-id":()=>e.id,sku:()=>e.sku,barcode:()=>e.barcode,stock:()=>e.quantityAvailable,quantity:()=>this.quantity};this.$(o("bind")).forEach(c=>{let d=l(c,"bind");d==="variant-image"?A(c,e.image?.url||t.featuredImage?.url,e.image?.altText||t.title):lt[d]&&h(c,lt[d]())}),this.$(o("option-selected")).forEach(c=>h(c,this.selected[l(c,"option-selected")]||"")),this.$(o("option-list")).forEach(c=>{let d=l(c,"option-list");m(c,o("option-value")).forEach(w=>{let P=l(w,"option-value");R(w,this.selected[d]===P);let y=!this.isValueAvailable(d,P);w.classList.toggle("is-unavailable",y),w.setAttribute("data-unavailable",y?"true":"false")})}),this.$(`select${o("option")}`).forEach(c=>{c.value=this.selected[l(c,"option")]||""}),this.$(`select${o("variant-select")}`).forEach(c=>{c.value=e.id}),this.$(o("purchase")).forEach(c=>{if(c.tagName==="SELECT"){c.value=this.purchase;return}let d=l(c,"purchase");R(c,d===this.purchase);let w=d==="subscription"?!this.subscriptionAvailable:t.requiresSellingPlan;c.classList.toggle("is-unavailable",w)}),this.$(o("plan-value")).forEach(c=>{let d=l(c,"plan-value"),w=this.plans.find(y=>y.id===d),P=!!L(e,d);R(c,i&&d===this.planId),c.classList.toggle("is-unavailable",!P),m(c,o("plan-bind")).forEach(y=>{let S=bt(w),dt=at(e,d),Lt=l(y,"plan-bind"),ht={name:()=>w?.name,description:()=>w?.description,price:()=>v(nt(e,d)),savings:()=>ct(dt.percentage),"savings-amount":()=>v(dt.amount),adjustment:()=>S?S.type==="percentage"?`${S.value}%`:v(S.value):""}[Lt];ht&&h(y,ht())})}),this.$(`select${o("plan")}`).forEach(c=>{c.value=i?this.planId:""});let ut={"in-stock":e.availableForSale,"out-of-stock":!e.availableForSale,backorder:e.availableForSale&&e.currentlyNotInStock,"on-sale":!!b,"not-on-sale":!b,"has-variants":t.variants.nodes.length>1,"single-variant":t.variants.nodes.length===1,"low-stock":e.quantityAvailable!=null&&e.quantityAvailable>0&&e.quantityAvailable<=5,"subscription-available":this.subscriptionAvailable,"subscription-unavailable":!this.subscriptionAvailable,"subscription-only":!!t.requiresSellingPlan,"one-time-available":!t.requiresSellingPlan,"subscription-selected":i,"one-time-selected":!i,"has-savings":i?O.percentage>0:!!b,loading:!1,ready:!0,error:!1,"not-found":!1};this.$(o("show")).forEach(c=>{let d=l(c,"show");d in ut&&($t.includes(d)&&!Et(c,this.root)||q(c,ut[d]))}),this.root.classList.toggle("is-subscription",i),this.root.setAttribute(f("purchase-mode"),i?"subscription":"one-time"),this.$(o("add-to-cart")).forEach(c=>{let d=!e.availableForSale;c.classList.toggle("is-disabled",d),c.setAttribute("aria-disabled",String(d)),"disabled"in c&&(c.disabled=d);let w=l(c,"soldout-text"),P=l(c,"subscribe-text");if(w||P){let y=c.querySelector(o("add-label"))||c;y.dataset.tetherLabel===void 0&&(y.dataset.tetherLabel=y.tagName==="INPUT"?y.value:y.textContent);let S=y.dataset.tetherLabel;i&&P&&(S=P),d&&w&&(S=w),c.getAttribute("aria-busy")!=="true"&&h(y,S)}}),g("variant-changed",{product:t,variant:e,selected:{...this.selected},purchase:this.purchase,sellingPlanId:i?this.planId:null},this.root)}};var X=()=>`${o("collection")},${o("products")},${o("recommendations")}`,xt=class{constructor(t){this.root=t,this.products=[],this.pageInfo=null,this.collection=null,k(t,"loading"),t.addEventListener("click",e=>{let i=e.target.closest?.(o("load-more"));i&&i.closest(X())===t&&(e.preventDefault(),W(i,()=>this.load(!0)))})}$(t){return K(this.root,t,X())}options(){let t=this.root,e={first:parseInt(l(t,"limit")||"",10)||void 0,sortKey:l(t,"sort")||void 0,reverse:l(t,"reverse")==="true"};return T(t,"collection")?e.collection=l(t,"collection"):e.query=l(t,"products")||"",e}async load(t=!1){let e=this.loadToken=(this.loadToken||0)+1,i=()=>e!==this.loadToken;try{if(T(this.root,"recommendations")){let n=l(this.root,"recommendations"),a=await F(n),r=await mt(a.id,l(this.root,"intent")||"RELATED");if(i())return;let u=parseInt(l(this.root,"limit")||"",10);this.products=u?r.slice(0,u):r}else{let n=await ft({...this.options(),after:t?this.pageInfo?.endCursor:null});if(i())return;this.collection=n.collection,this.pageInfo=n.pageInfo,this.products=t?[...this.products,...n.products]:n.products}this.render(),k(this.root,"ready"),g("list-ready",{products:this.products,collection:this.collection},this.root)}catch(n){if(i())return;t||k(this.root,G(n)?"not-found":"error",n),I(n,"list load failed"),g("error",{error:n,context:"list"},this.root)}}render(){let t=this.collection;this.$(o("collection-bind")).forEach(i=>{switch(l(i,"collection-bind")){case"title":return h(i,t?.title||"");case"description":i.innerHTML=t?.descriptionHtml||"";return;case"image":return A(i,t?.image?.url,t?.image?.altText||t?.title);case"count":return h(i,this.products.length);default:return}}),this.$(o("list")).forEach(i=>{C(i,this.products,(n,a)=>{n.setAttribute(f("product"),a.handle),n.removeAttribute(f("state"))}),g("list-rendered",{container:i},this.root)});let e=this.products.length===0;this.$(o("show")).forEach(i=>{let n=l(i,"show");n==="list-empty"&&(i.style.display=e?"":"none"),n==="list-filled"&&(i.style.display=e?"none":"")}),this.$(o("load-more")).forEach(i=>{i.style.display=this.pageInfo?.hasNextPage?"":"none"}),_("list")}};var Z=new WeakSet,he=new WeakSet,tt=new Set,qt=new Set,kt=new WeakMap;function Ut(){tt.forEach(s=>{s.isConnected||tt.delete(s)}),qt.forEach(s=>{s.root.isConnected||qt.delete(s)})}function pe(s,t){return[...s.matches?.(t)?[s]:[],...m(s,t)]}function Bt(s){if(Z.has(s))return!1;let{key:t,fromUrl:e,raw:i}=At(s);if(!t)return e?(Z.add(s),k(s,"not-found"),g("product-not-found",{key:null,source:i},s)):s.closest(X())||pt(`[${f("product")}] without a value. Bind it to the Shopify handle field, or use "url:handle".`,s),!1;Z.add(s),tt.add(s);let n={};kt.set(s,n);let a=()=>kt.get(s)===n;return z.get(s)?s.setAttribute(f("state"),"updating"):k(s,"loading"),F(t).then(r=>{if(!a())return;let u=z.get(s);u?u.update(r):new J(s,r),Mt(s,r)}).catch(r=>{if(!a())return;let u=G(r);k(s,u?"not-found":"error",r),u?(g("product-not-found",{key:t,source:i},s),e?Q(`product not found: ${t}`):I(r,`product ${t}`)):(I(r,`product ${t}`),g("error",{error:r,context:"product",key:t},s))}),!0}function Mt(s,t){let e=l(s,"page-title");!e||typeof document>"u"||(document.title=e.replace(/\{title\}/g,t.title).replace(/\{vendor\}/g,t.vendor||"").replace(/\{type\}/g,t.productType||""))}function fe(){Ut(),tt.forEach(s=>{s.getAttribute(f("state"))!=="error"||s.getAttribute(f("error"))==="not-found"||(Z.delete(s),Bt(s))})}export{Ot as a,Pt as b,z as c,X as d,xt as e,Z as f,he as g,tt as h,qt as i,Ut as j,pe as k,Bt as l,fe as m};
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
var Z=Object.defineProperty;var ee=(e,t)=>{for(var r in t)Z(e,r,{get:t[r],enumerable:!0})};var _={domain:"",token:"",apiVersion:"2026-07",country:void 0,language:void 0,locale:void 0,markets:null,prefix:"tether",productPath:"/product",collectionPath:"/collection",metafields:[],imagesFirst:25,variantsFirst:100,sellingPlanGroupsFirst:10,sellingPlansFirst:25,collectionFirst:24,retries:2,storageKey:null,cartDrawerOpenClass:"is-open",openCartOnAdd:!0,cartFocus:!0,checkoutTarget:"_self",defaultPurchase:"one-time",autoMount:!0,observe:!1,partsUrl:null,formatMoney:null,onLayoutChange:null,onCartOpen:null,onCartClose:null,onLineEnter:null,onLineLeave:null,onValueChange:null,onError:null,debug:!1},o={..._};function Se(e={}){return Object.keys(o).forEach(t=>delete o[t]),Object.assign(o,_,e),o.domain&&(o.domain=String(o.domain).replace(/^https?:\/\//,"").replace(/\/.*$/,"")),o.storageKey||(o.storageKey=`${o.prefix}:cart:${o.domain}`),o.apiVersion==="unstable"&&b('apiVersion "unstable" changes without notice. Pin a dated version (e.g. 2026-07).'),o}var Ce=()=>!!(o.domain&&o.token);function S(...e){o.debug&&console.log(`[${o.prefix}]`,...e)}function b(...e){console.warn(`[${o.prefix}]`,...e)}function Te(e,t){if(b(t,e&&e.message?e.message:e),typeof o.onError=="function")try{o.onError(e,t)}catch{}}var m=class extends Error{constructor(t,{status:r,errors:n,userErrors:i,code:s}={}){super(t),this.name="TetherError",this.code=s||(r?`HTTP_${r}`:"ERROR"),this.status=r,this.errors=n,this.userErrors=i}};function te(){return`https://${o.domain}/api/${o.apiVersion}/graphql.json`}function re(e,t=o){let r=[];return t.country&&r.push(`country: ${String(t.country).toUpperCase()}`),t.language&&r.push(`language: ${String(t.language).toUpperCase().replace("-","_")}`),!r.length||/@inContext/.test(e)?e:e.replace(/^(\s*(?:query|mutation)\s+\w+\s*(?:\([^)]*\))?)/,`$1 @inContext(${r.join(", ")})`)}var D=e=>new Promise(t=>setTimeout(t,e));async function v(e,t={},{context:r=!0}={}){if(!o.domain||!o.token)throw new m("Tether is not configured: domain and token are required",{code:"NOT_CONFIGURED"});let n=JSON.stringify({query:r?re(e):e,variables:t}),i=0;for(;;){let s;try{s=await fetch(te(),{method:"POST",headers:{"Content-Type":"application/json","X-Shopify-Storefront-Access-Token":o.token},body:n})}catch(g){if(i++<o.retries){await D(300*2**i);continue}throw new m(`Network error: ${g.message}`,{code:"NETWORK"})}if(s.status===430)throw new m("Shopify security rejection (430)",{status:430});if((s.status===429||s.status>=500)&&i++<o.retries){S("retry",s.status,i),await D(300*2**i);continue}let f;try{f=await s.json()}catch{throw new m(`Invalid response (${s.status})`,{status:s.status})}if(f.errors?.length){if(f.errors.some(a=>/quantityAvailable/.test(a.message||""))&&/\bquantityAvailable\b/.test(n))return S("no inventory permission, retrying without quantityAvailable"),v(e.replace(/\bquantityAvailable\b/g,""),t,{context:r});if(f.errors.some(a=>a.extensions?.code==="THROTTLED")&&i++<o.retries){await D(500*2**i);continue}throw new m(f.errors.map(a=>a.message).join(" | "),{status:s.status,errors:f.errors})}if(!s.ok)throw new m(`HTTP ${s.status}`,{status:s.status});return f.data}}function Fe(e,t){if(!e)throw new m(`${t}: empty response`);let r=e.userErrors||[];if(r.length)throw new m(r.map(n=>n.message).join(" | "),{userErrors:r});return e}var u="amount currencyCode",P="url altText width height";function ne(){return(o.metafields||[]).map(e=>{if(typeof e=="string"){let t=e.indexOf(".");return{namespace:e.slice(0,t),key:e.slice(t+1)}}return e}).filter(e=>e.namespace&&e.key)}function oe(){let e=ne();return e.length?`metafields(identifiers:[${e.map(r=>`{namespace: ${JSON.stringify(r.namespace)}, key: ${JSON.stringify(r.key)}}`).join(", ")}]){namespace key type value reference{... on MediaImage{image{${P}}}}}`:""}var ie=` id name description recurringDeliveries options{name value}priceAdjustments{orderCount adjustmentValue{__typename ... on SellingPlanPercentagePriceAdjustment{adjustmentPercentage}... on SellingPlanFixedAmountPriceAdjustment{adjustmentAmount{${u}}}... on SellingPlanFixedPriceAdjustment{price{${u}}}}}`,se=` sellingPlan{id}checkoutChargeAmount{${u}}remainingBalanceChargeAmount{${u}}priceAdjustments{price{${u}}compareAtPrice{${u}}perDeliveryPrice{${u}}unitPrice{${u}}}`;function T(){return` fragment TetherProduct on Product{id handle title description descriptionHtml productType vendor tags availableForSale publishedAt onlineStoreUrl requiresSellingPlan seo{title description}featuredImage{${P}}images(first:${o.imagesFirst}){nodes{${P}}}options{name optionValues{name swatch{color image{previewImage{${P}}}}}}priceRange{minVariantPrice{${u}}maxVariantPrice{${u}}}compareAtPriceRange{minVariantPrice{${u}}maxVariantPrice{${u}}}${oe()} sellingPlanGroups(first:${o.sellingPlanGroupsFirst}){nodes{name appName options{name values}sellingPlans(first:${o.sellingPlansFirst}){nodes{${ie}}}}}variants(first:${o.variantsFirst}){nodes{id title availableForSale currentlyNotInStock quantityAvailable sku barcode selectedOptions{name value}image{${P}}price{${u}}compareAtPrice{${u}}unitPrice{${u}}sellingPlanAllocations(first:${o.sellingPlansFirst}){nodes{${se}}}}}}`}function je(){return` fragment TetherCart on Cart{id checkoutUrl totalQuantity note createdAt updatedAt attributes{key value}buyerIdentity{countryCode email}discountCodes{code applicable}cost{subtotalAmount{${u}}totalAmount{${u}}checkoutChargeAmount{${u}}}lines(first:100){nodes{id quantity attributes{key value}cost{totalAmount{${u}}subtotalAmount{${u}}amountPerQuantity{${u}}compareAtAmountPerQuantity{${u}}}discountAllocations{discountedAmount{${u}}}sellingPlanAllocation{sellingPlan{id name description recurringDeliveries options{name value}}checkoutChargeAmount{${u}}priceAdjustments{price{${u}}compareAtPrice{${u}}perDeliveryPrice{${u}}}}merchandise{... on ProductVariant{id title sku availableForSale requiresShipping image{${P}}price{${u}}compareAtPrice{${u}}selectedOptions{name value}product{id title handle vendor featuredImage{${P}}}}}... on ComponentizableCartLine{lineComponents{id quantity merchandise{... on ProductVariant{id title product{title handle}}}}}}}}`}function G(){return`
|
|
2
|
-
id handle title description descriptionHtml
|
|
3
|
-
image { ${P} }
|
|
4
|
-
seo { title description }`}var x=new Map,B=[],U=new Map,q=!1,C=0,R=e=>typeof e=="string"&&e.startsWith("gid://");function ae(e){let t=String(e??"").trim();if(!t)return null;if(R(t))return t;if(/^\d+$/.test(t))return`gid://shopify/Product/${t}`;let r=t.match(/\/products\/([^/?#]+)/);return r?r[1]:t}function E(e){if(!e)return;let t=Promise.resolve(e);x.set(e.handle,t),x.set(e.id,t)}function qe(e){let t=ae(e);if(!t)return Promise.reject(new m("empty product key",{code:"NOT_FOUND"}));if(x.has(t))return x.get(t);let r=new Promise((n,i)=>{U.set(t,{resolve:n,reject:i}),B.push(t),q||(q=!0,setTimeout(ce,0))});return x.set(t,r),r}async function ce(){q=!1;let e=C,t=B.splice(0);if(!t.length)return;let r=t.filter(R),n=t.filter(d=>!R(d)),i=[],s=[],p={};r.length&&(i.push("$ids: [ID!]!"),p.ids=r,s.push("byId: nodes(ids: $ids) { ... on Product { ...TetherProduct } }")),n.forEach((d,a)=>{i.push(`$h${a}: String!`),p[`h${a}`]=d,s.push(`h${a}: product(handle: $h${a}) { ...TetherProduct }`)});let f=`query TetherProducts(${i.join(",")}){${s.join(" ")}}${T()}`;S("fetch products",t);let g=(d,a)=>{let l=U.get(d);U.delete(d),l&&a(l)};try{let d=await v(f,p),a=new Map;(d.byId||[]).forEach(l=>{l&&a.set(l.id,l)}),n.forEach((l,h)=>{d[`h${h}`]&&a.set(l,d[`h${h}`])}),t.forEach(l=>g(l,h=>{a.has(l)?(e===C&&E(a.get(l)),h.resolve(a.get(l))):(e===C&&x.delete(l),h.reject(new m(`Product not found: ${l}`,{code:"NOT_FOUND"})))}))}catch(d){t.forEach(a=>g(a,l=>{e===C&&x.delete(a),l.reject(d)}))}}function Re(){x.clear(),C++}async function Ke({collection:e,query:t,first:r=o.collectionFirst,after:n=null,sortKey:i,reverse:s=!1,filters:p}={}){let f=T();if(e){let d=await v(`query TetherCollection($handle:String!,$first:Int!,$after:String,$sortKey:ProductCollectionSortKeys,$reverse:Boolean,$filters:[ProductFilter!]){collection(handle:$handle){${G()} products(first:$first,after:$after,sortKey:$sortKey,reverse:$reverse,filters:$filters){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}}${f}`,{handle:e,first:r,after:n,sortKey:i||void 0,reverse:s,filters:p||void 0});if(!d.collection)throw new m(`Collection not found: ${e}`,{code:"NOT_FOUND"});let{products:a,...l}=d.collection;return a.nodes.forEach(E),{collection:l,products:a.nodes,pageInfo:a.pageInfo}}let g=await v(`query TetherSearch($first:Int!,$after:String,$query:String,$sortKey:ProductSortKeys,$reverse:Boolean){products(first:$first,after:$after,query:$query,sortKey:$sortKey,reverse:$reverse){nodes{...TetherProduct}pageInfo{hasNextPage endCursor}}}${f}`,{first:r,after:n,query:t||void 0,sortKey:i||void 0,reverse:s});return g.products.nodes.forEach(E),{collection:null,products:g.products.nodes,pageInfo:g.products.pageInfo}}async function Ve(e,t="RELATED"){let n=(await v(`query TetherRecs($id:ID!,$intent:ProductRecommendationIntent){productRecommendations(productId:$id,intent:$intent){...TetherProduct}}${T()}`,{id:e,intent:t})).productRecommendations||[];return n.forEach(E),n}var ge={};ee(ge,{GID:()=>H,allPlans:()=>F,allocationFor:()=>k,findPlan:()=>me,hasPlans:()=>ue,planAdjustment:()=>fe,planCheckoutCharge:()=>de,planPrice:()=>J,planSavings:()=>pe,plansForVariant:()=>le,toGid:()=>N});var H={product:e=>N(e,"Product"),variant:e=>N(e,"ProductVariant"),sellingPlan:e=>N(e,"SellingPlan")};function N(e,t){let r=String(e??"").trim();return r?r.startsWith("gid://")?r:/^\d+$/.test(r)?`gid://shopify/${t}/${r}`:r:null}function F(e){return(e?.sellingPlanGroups?.nodes||[]).flatMap(r=>(r.sellingPlans?.nodes||[]).map(n=>({...n,group:r})))}function ue(e){return F(e).length>0}function k(e,t){return!e||!t?null:(e.sellingPlanAllocations?.nodes||[]).find(r=>r.sellingPlan?.id===t)||null}function le(e,t){return F(e).filter(r=>k(t,r.id))}function J(e,t){let n=k(e,t)?.priceAdjustments?.[0];return n?.perDeliveryPrice||n?.price||e?.price||null}function de(e,t){let r=k(e,t);return r?.checkoutChargeAmount||r?.priceAdjustments?.[0]?.price||e?.price||null}function pe(e,t){let r=parseFloat(e?.price?.amount),n=parseFloat(J(e,t)?.amount);return!Number.isFinite(r)||!Number.isFinite(n)||r<=0||n>=r?{amount:null,percentage:0}:{amount:{amount:(r-n).toFixed(2),currencyCode:e.price.currencyCode},percentage:Math.round((1-n/r)*100)}}function fe(e){let t=e?.priceAdjustments?.[0]?.adjustmentValue;if(!t)return null;switch(t.__typename){case"SellingPlanPercentagePriceAdjustment":return{type:"percentage",value:t.adjustmentPercentage};case"SellingPlanFixedAmountPriceAdjustment":return{type:"amount",value:t.adjustmentAmount};case"SellingPlanFixedPriceAdjustment":return{type:"price",value:t.price};default:return null}}function me(e,t){if(!t)return null;let r=F(e),n=H.sellingPlan(t),i=String(t).trim().toLowerCase();return r.find(s=>s.id===n)||r.find(s=>s.name.toLowerCase()===i)||r.find(s=>(s.options||[]).some(p=>String(p.value).toLowerCase()===i))||null}var $=e=>`${o.prefix}-${e}`,j=(e,t)=>t===void 0?`[${$(e)}]`:`[${$(e)}="${t}"]`,O=(e,t)=>e.getAttribute($(t)),Ge=(e,t)=>e.hasAttribute($(t)),he=()=>`data-${o.prefix}-key`;function Be(e,t){let r=o.productPath||"/product";if(!/\{(handle|id)\}/.test(r))return`${r.replace(/\/$/,"")}/${e}`;let n=t?String(t).split("/").pop():"";return r.replace(/\{handle\}/g,encodeURIComponent(e||"")).replace(/\{id\}/g,encodeURIComponent(n))}var Q=(e,t)=>e?[...e.querySelectorAll(t)]:[],K=(e,t,r)=>Q(e,t).filter(n=>n.closest(r)===e);function ye(e,t,r=document){r.dispatchEvent(new CustomEvent(`${o.prefix}:${e}`,{detail:t,bubbles:!0}))}function He(e){if(!e)return"";if(typeof o.formatMoney=="function")return o.formatMoney(e,o.locale);let t=parseFloat(e.amount);if(Number.isNaN(t))return"";try{return new Intl.NumberFormat(o.locale||void 0,{style:"currency",currency:e.currencyCode}).format(t)}catch{return`${e.currencyCode} ${t.toFixed(2)}`}}function Je(e){if(!e)return"";try{return new Date(e).toLocaleDateString(o.locale||void 0)}catch{return""}}function $e(e,t){if(!e||!t)return e;let r=t.getAttribute("max-width"),n=t.getAttribute("max-height"),i=t.getAttribute("crop");if(!r&&!n)return e;try{let s=new URL(e);return r&&s.searchParams.set("width",r),n&&s.searchParams.set("height",n),i&&s.searchParams.set("crop",i),s.toString()}catch{return e}}function We(e,t,r){if(!t)return;let n=$e(t,e);e.tagName==="IMG"?(e.removeAttribute("srcset"),e.removeAttribute("sizes"),e.setAttribute("src",n),r!=null&&e.setAttribute("alt",r),e.getAttribute("loading")||e.setAttribute("loading","lazy")):e.style.backgroundImage=`url("${n}")`}function Pe(e,t){let r=t==null?"":String(t);e.tagName==="INPUT"||e.tagName==="TEXTAREA"?e.value=r:e.textContent=r}var W=e=>e.tagName==="INPUT"||e.tagName==="TEXTAREA"?e.value:e.textContent;function ze(e,t){let r=W(e);Pe(e,t);let n=W(e);if(r!==n&&e.dataset.tetherSeen&&typeof o.onValueChange=="function")try{o.onValueChange(e,r)}catch{}e.dataset.tetherSeen="1"}function X(e,t){let r="tetherPrevDisplay";t?e.dataset[r]!==void 0?(e.style.display=e.dataset[r],delete e.dataset[r]):e.style.display==="none"&&(e.style.display=""):(e.dataset[r]===void 0&&(e.dataset[r]=e.style.display||""),e.style.display="none")}function Qe(e,t){e.classList.toggle("is-selected",t),e.setAttribute("aria-pressed",t?"true":"false"),e.tagName==="INPUT"&&(e.type==="radio"||e.type==="checkbox")&&(e.checked=t);let r=e.querySelector?.('input[type="radio"],input[type="checkbox"]');r&&(r.checked=t)}var I=new WeakMap,Xe=e=>!!I.get(e);function Y(e){if(!I.has(e)){let t=e.firstElementChild?.cloneNode(!0)??null;I.set(e,t),e.innerHTML=""}return I.get(e)}function Ye(e,t,r){let n=Y(e);if(!n){b("repeater without a template child",e);return}e.innerHTML="",t.forEach((i,s)=>{let p=n.cloneNode(!0);r(p,i,s),e.appendChild(p)})}function Ze(e,t,r,n,{onEnter:i,onLeave:s}={}){let p=Y(e);if(!p){b("repeater without a template child",e);return}let f=he(),g=`data-${o.prefix}-leaving`,d=()=>[...e.children].filter(c=>c.hasAttribute(f)&&!c.hasAttribute(g)),a=new Map(d().map(c=>[c.getAttribute(f),c])),l=[],h=new Set;t.forEach((c,y)=>{let A=String(r(c,y)),w=a.get(A);w?a.delete(A):(w=p.cloneNode(!0),w.setAttribute(f,A),h.add(w)),n(w,c,y,h.has(w)),l.push(w)});let L=l.filter(c=>!h.has(c)),M=d().filter(c=>L.includes(c));if(!(M.length===L.length&&M.every((c,y)=>c===L[y]))){a.forEach(c=>c.remove()),l.forEach(c=>e.appendChild(c));return}l.forEach((c,y)=>{if(!h.has(c))return;let A=l[y-1];if(A&&A.parentNode===e?A.after(c):e.prepend(c),i)try{i(c)}catch{}}),a.forEach(c=>{c.setAttribute(g,""),c.removeAttribute(f),c.style.pointerEvents="none",c.inert=!0;let y=()=>c.remove();if(s)try{s(c,y)}catch{y()}else y()})}var z=null;function xe(e){clearTimeout(z),z=setTimeout(()=>{typeof o.onLayoutChange=="function"?o.onLayoutChange(e):window.ScrollTrigger?.refresh&&window.ScrollTrigger.refresh(),ye("layout-changed",{reason:e})},120)}function et(e,t){let r=Q(e,"img").filter(s=>!s.complete);if(!r.length)return;let n=r.length,i=()=>{--n===0&&xe(`${t}:images`)};r.forEach(s=>{s.addEventListener("load",i,{once:!0}),s.addEventListener("error",i,{once:!0})})}function tt(e,t){return e.getAttribute("aria-busy")==="true"?Promise.resolve():(e.setAttribute("aria-busy","true"),Promise.resolve().then(t).finally(()=>e.removeAttribute("aria-busy")))}function rt(e,t){let r=e&&O(e,t);if(!r)return null;try{return JSON.parse(r)}catch{return b(`invalid JSON in ${$(t)}`,e),null}}function nt(e){return e?Array.isArray(e)?e.map(t=>({key:String(t.key),value:String(t.value??"")})):Object.entries(e).map(([t,r])=>({key:t,value:String(r??"")})):[]}function st(e,t=window.location){let r=(O(e,"product")||"").trim();if(!r.toLowerCase().startsWith("url:"))return{key:r||null,fromUrl:!1,raw:r};let n=r.slice(4).trim(),i=null;if(n.toLowerCase()==="path"){let s=t.pathname.split("/").filter(Boolean);i=s.length?Ae(s[s.length-1]):null}else i=new URLSearchParams(t.search).get(n||"handle");return i=i&&i.trim()?i.trim():null,{key:i,fromUrl:!0,raw:r}}function Ae(e){try{return decodeURIComponent(e)}catch{return e}}var we=["loading","ready","error","not-found"],at=e=>e?.code==="NOT_FOUND",V=()=>["product","collection","products","recommendations"].map(e=>j(e)).join(","),ct=(e,t)=>e===t||e.parentElement?.closest(V())===t;function ut(e,t,r){let n=t==="not-found";e.setAttribute($("state"),n?"error":t),t==="error"||n?e.setAttribute($("error"),n?"not-found":r?.code||"error"):e.removeAttribute($("error")),K(e,j("show"),V()).forEach(i=>{let s=O(i,"show");if(!we.includes(s))return;let p=s===t;X(i,p),p&&typeof getComputedStyle=="function"&&getComputedStyle(i).display==="none"&&(i.style.display=i.getAttribute($("display"))||"block")}),(t==="error"||n)&&K(e,j("add-to-cart"),V()).forEach(i=>{i.classList.add("is-disabled"),i.setAttribute("aria-disabled","true"),"disabled"in i&&(i.disabled=!0)})}export{o as a,Se as b,Ce as c,S as d,b as e,Te as f,m as g,te as h,re as i,v as j,Fe as k,je as l,ae as m,E as n,qe as o,Re as p,Ke as q,Ve as r,H as s,F as t,k as u,J as v,de as w,pe as x,fe as y,me as z,ge as A,$ as B,j as C,O as D,Ge as E,Be as F,Q as G,K as H,ye as I,He as J,Je as K,We as L,Pe as M,ze as N,X as O,Qe as P,Xe as Q,Ye as R,Ze as S,xe as T,et as U,tt as V,rt as W,nt as X,st as Y,we as Z,at as _,V as $,ct as aa,ut as ba};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{c as V}from"./chunk-KYQ5OGBX.js";import{$ as P,B as u,C as a,D as g,G as c,Q as v,Y as y,a as l,c as b,h as k,j as w,o as S,t as E}from"./chunk-LI3YDMGF.js";var D=e=>{let o=/^(\d{4})-(01|04|07|10)$/.exec(String(e||""));return o?{y:+o[1],m:+o[2]}:null},A=(e,o)=>`${e}-${String(o).padStart(2,"0")}`;function x(e,o=1){let n=D(e);if(!n)throw new Error(`not a quarterly Shopify API version: ${e}`);let p=n.y*4+(n.m-1)/3+o;return A(Math.floor(p/4),p%4*3+1)}function M(e,o=new Date){if(e==="unstable")return{version:e,ok:!1,note:'"unstable" changes without notice: pin a dated version such as 2026-07'};let n=D(e);if(!n)return{version:e,ok:!1,note:"not a quarterly version (YYYY-01, -04, -07 or -10)"};let p=new Date(Date.UTC(n.y,n.m-1,1)),r=new Date(Date.UTC(n.y+1,n.m-1,1)),h=(r-o)/(30.44*24*3600*1e3),i=`supported until about ${r.toISOString().slice(0,7)}`;return p>o?i="not released yet (release candidate at best)":h<=0?i="no longer supported: Shopify falls back to the oldest supported version. Upgrade (see README)":h<3&&(i=`${i}: plan the upgrade to ${x(e)} or later (see README)`),{version:e,ok:p<=o&&h>0,monthsLeft:Math.round(h),next:x(e),note:i}}var q=e=>e?`${String(e).slice(0,4)}\u2026${String(e).slice(-4)} (${String(e).length} chars)`:"(missing)";function C(e){let o=e.id?`#${e.id}`:"",n=e.className&&typeof e.className=="string"?`.${e.className.trim().split(/\s+/).slice(0,2).join(".")}`:"";return`<${e.tagName.toLowerCase()}${o}${n}>`}function I(e=document){let o=[],n=(t,s,d)=>o.push({level:t,message:s,element:d?C(d):void 0,el:d}),p=t=>!!t.closest(a("product")),r=`${a("collection")},${a("products")},${a("recommendations")}`;c(e,a("product")).forEach(t=>{if(t.closest(r)&&t.closest(a("list")))return;let{key:s,fromUrl:d,raw:f}=y(t);f?d&&!s&&n("warn",`${u("product")}="${f}" but this URL has no value for it (e.g. ?handle=my-product).`,t):n("error",`${u("product")} has no value. Bind it to the Shopify handle (CMS field), or use "url:handle".`,t)}),["bind","option-list","option","variant-select","add-to-cart","purchase","plan-list","plan","qty","line-attribute"].forEach(t=>{c(e,a(t)).forEach(s=>{p(s)||t==="line-attribute"&&s.closest(a("group"))||n("warn",`${u(t)} outside any ${u("product")}: it does nothing here.`,s)})}),c(e,a("group-add")).forEach(t=>{t.closest(a("group"))||n("warn",`${u("group-add")} outside a ${u("group")}.`,t)}),[...c(e,a("option-list")),...c(e,a("plan-list")),...c(e,a("cart-lines")),...c(e,a("list"))].forEach(t=>{!t.firstElementChild&&!v(t)&&n("warn","Repeater without a template child: put one example element inside it.",t)}),c(e,r).forEach(t=>{c(t,a("list")).length||n("error",`List without a ${u("list")} child: products have nowhere to render.`,t)});let i=new Set(["in-stock","out-of-stock","backorder","low-stock","on-sale","not-on-sale","has-variants","single-variant","subscription-available","subscription-unavailable","subscription-only","one-time-available","subscription-selected","one-time-selected","has-savings","loading","ready","error","not-found","cart-empty","cart-filled","cart-has-subscription","cart-no-subscription","cart-has-discount","cart-error","list-empty","list-filled"]);return c(e,a("show")).forEach(t=>{let s=g(t,"show");i.has(s)||n("warn",`Unknown ${u("show")}="${s}".`,t)}),c(e,a("bind")).forEach(t=>{let s=g(t,"bind")||"";if(!s.startsWith("metafield:"))return;let d=s.slice(10);(l.metafields||[]).some(m=>(typeof m=="string"?m:`${m.namespace}.${m.key}`)===d)||n("error",`${u("bind")}="${s}" but "${d}" is not in config.metafields, so it stays empty.`,t)}),c(e,a("cart-open")).length&&!c(e,a("cart-drawer")).length&&n("warn",`${u("cart-open")} exists but there is no ${u("cart-drawer")} on this page.`),c(e,a("cart-drawer")).length>1&&n("warn",`More than one ${u("cart-drawer")}.`),c(e,`[${u("state")}="error"]`).forEach(t=>{t.matches(P())&&n("error",`Scope failed to load (${g(t,"error")||"error"}).`,t)}),o}async function F({root:e=document,silent:o=!1,version:n,now:p=new Date}={}){let r={version:n,config:{domain:l.domain||"(missing)",token:q(l.token),apiVersion:l.apiVersion,endpoint:l.domain?k():null,country:l.country,language:l.language,locale:l.locale,markets:l.markets||null,prefix:l.prefix,productPath:l.productPath,metafields:l.metafields,autoMount:l.autoMount,observe:l.observe,onError:typeof l.onError=="function"},apiVersion:M(l.apiVersion,p),market:l.markets?.detect?V(l.markets):null,reachable:!1,shop:null,latencyMs:null,products:[],sellingPlans:[],problems:[]};if(!b())r.problems.push({level:"error",message:"TETHER_CONFIG needs domain and token."});else{let h=Date.now();try{let i=await w("query TetherDiagnose { shop { name primaryDomain { url } paymentSettings { currencyCode } } }",{},{context:!1});r.reachable=!0,r.shop={name:i.shop?.name,url:i.shop?.primaryDomain?.url,currency:i.shop?.paymentSettings?.currencyCode}}catch(i){let t=i.status===401||i.status===403?"Token rejected: use the public Storefront token from the Headless channel, not an Admin token.":i.status===404?"Store or API version not found: check domain (x.myshopify.com) and apiVersion.":"";r.problems.push({level:"error",message:`Storefront API not reachable: ${i.message}${t?`. ${t}`:""}`})}if(r.latencyMs=Date.now()-h,r.reachable){let i=[...new Set(c(e,a("product")).map(s=>y(s).key).filter(Boolean))].slice(0,20);(await Promise.allSettled(i.map(s=>S(s)))).forEach((s,d)=>{if(s.status==="fulfilled"){let f=s.value,m=E(f);r.products.push({key:i[d],handle:f.handle,title:f.title,plans:m.length,requiresSellingPlan:f.requiresSellingPlan}),m.forEach($=>r.sellingPlans.push({product:f.handle,group:$.group?.name,plan:$.name,id:$.id}))}else r.products.push({key:i[d],error:s.reason?.message}),r.problems.push({level:"error",message:`Product "${i[d]}": ${s.reason?.message}. Is it published to the Headless channel?`})})}}return(!r.apiVersion.ok||(r.apiVersion.monthsLeft??99)<3)&&r.problems.push({level:r.apiVersion.ok?"warn":"error",message:`apiVersion ${l.apiVersion}: ${r.apiVersion.note}`}),typeof document<"u"&&r.problems.push(...I(e)),o||T(r),r}function T(e){let o=console,n=e.problems.filter(p=>p.level==="error").length;o.group?.(`[${l.prefix}] diagnose: ${n?`${n} error(s)`:"no errors"}, ${e.problems.length-n} warning(s)`),o.log("version",e.version,"| API",e.apiVersion.version,`(${e.apiVersion.note})`),o.log("config",e.config),e.market&&o.log("market detected",e.market,"(in use:",e.config.country,e.config.language,")"),o.log(e.reachable?`Storefront API reachable: ${e.shop?.name} (${e.latencyMs} ms)`:"Storefront API NOT reachable"),e.products.length&&(o.table||o.log)(e.products),e.sellingPlans.length?(o.table||o.log)(e.sellingPlans):e.reachable&&o.log("No selling plans on the products of this page."),e.problems.forEach(p=>(p.level==="error"?o.error:o.warn)(p.message,p.el||"")),o.groupEnd?.()}export{M as apiVersionStatus,I as checkMarkup,F as diagnose};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as l,d as E,f as k,h as z,i as A,j as M,k as h,l as N}from"./chunk-GHIUMD5H.js";import{a as w,b as P,c as T,d as x,e as I,f as g}from"./chunk-KYQ5OGBX.js";import{B as y,C as p,D as v,I as L,a as n,f,g as C,p as b}from"./chunk-LI3YDMGF.js";function S(u=document){let i=h(u,`select${p("country-select")}`),d=h(u,p("country-bind"));return!i.length&&!d.length?Promise.resolve():g().then(t=>{let a=(n.country||t.country?.isoCode||"").toUpperCase(),s=[...t.availableCountries||[]].sort((e,r)=>e.name.localeCompare(r.name,n.locale||void 0)),c=s.find(e=>e.isoCode===a)||t.country,o=(e,r)=>e.replace(/\{name\}/g,r.name).replace(/\{code\}/g,r.isoCode).replace(/\{currency\}/g,r.currency?.isoCode||"").replace(/\{symbol\}/g,r.currency?.symbol||"");i.forEach(e=>{let r=e.getAttribute(y("country-label"))||"{name} ({currency})";e.innerHTML="",s.forEach(m=>e.appendChild(new Option(o(r,m),m.isoCode))),e.value=a,e.value!==a&&(e.selectedIndex=-1),e.disabled=s.length<2&&!e.hasAttribute(y("always-enabled"))}),d.forEach(e=>{let r=v(e,"country-bind")||"name",m={name:c?.name,code:c?.isoCode,currency:c?.currency?.isoCode,symbol:c?.currency?.symbol}[r];e.textContent=m||""})}).catch(t=>f(t,"country select"))}async function V(u,{language:i,remember:d=!0}={}){let t=w(u);if(!t)throw new C(`Not a country code: ${u}`,{code:"INVALID_COUNTRY"});let a=i===void 0?n.language:P(i),s=t!==String(n.country||"").toUpperCase()||a!==n.language;if(n.country=t,n.language=a||void 0,d&&x({country:t,language:a}),!s)return S(),l.state;b();let c=[];return i!==void 0&&I(),l.id()&&c.push(l.setBuyerIdentity({countryCode:t},{create:!1}).catch(o=>f(o,"cart country"))),M(),A.forEach(o=>c.push(o.load())),z.forEach(o=>{o.closest(E())||(k.delete(o),N(o))}),S(),L("country-changed",{country:t,language:n.language}),await Promise.all(c),l.state}export{T as detectMarket,g as getLocalization,S as renderCountrySelects,V as setCountry};
|