@occluze/tether 0.6.8 → 0.7.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 +11 -11
- package/dist/esm/astro.js +2 -2
- package/dist/esm/chunks/account-UIPWJQ3Y.js +1 -0
- package/dist/esm/chunks/account-pages-6ROA6Y4V.js +1 -0
- package/dist/esm/chunks/analytics-2V632IRV.js +1 -0
- package/dist/esm/chunks/chunk-4GCXM764.js +1 -0
- package/dist/esm/chunks/chunk-56FCLJJD.js +1 -0
- package/dist/esm/chunks/chunk-5Z5O3767.js +1 -0
- package/dist/esm/chunks/chunk-6OZJCAGZ.js +1 -0
- package/dist/esm/chunks/chunk-A5SYCVQM.js +1 -0
- package/dist/esm/chunks/chunk-BA4LQJJQ.js +1 -0
- package/dist/esm/chunks/chunk-BFTZR4OB.js +4 -0
- package/dist/esm/chunks/chunk-C5JYQCWH.js +1 -0
- package/dist/esm/chunks/chunk-E5YO6274.js +1 -0
- package/dist/esm/chunks/chunk-MPDWM2NN.js +1 -0
- package/dist/esm/chunks/chunk-T44MUL4Y.js +1 -0
- package/dist/esm/chunks/chunk-YLUULATY.js +1 -0
- package/dist/esm/chunks/diagnose-3FEKRIEK.js +14 -0
- package/dist/esm/chunks/extras-6POFBNOP.js +1 -0
- package/dist/esm/chunks/markets-ui-FD6NI2OG.js +1 -0
- package/dist/esm/chunks/search-UJ73YBQ5.js +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +2 -2
- package/dist/presets/barba.esm.js +19 -23
- package/dist/tether.account-pages.js +320 -365
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +242 -491
- package/dist/tether.account.min.js +1 -1
- package/dist/tether.analytics.js +66 -88
- package/dist/tether.analytics.min.js +1 -1
- package/dist/tether.diagnose.js +36 -33
- package/dist/tether.diagnose.min.js +2 -2
- package/dist/tether.extras.js +28 -36
- package/dist/tether.extras.min.js +1 -1
- package/dist/tether.js +1270 -894
- package/dist/tether.markets.js +73 -90
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +0 -0
- package/dist/tether.min.js.map +4 -4
- package/dist/tether.search.js +44 -68
- package/dist/tether.search.min.js +1 -1
- package/dist/types/server/src/shared/account-orders.d.ts +44 -0
- package/dist/types/server/src/shared/account-routes.d.ts +153 -0
- package/dist/types/server/src/shared/api-versions.d.ts +13 -0
- package/dist/types/server/src/shared/error-message.d.ts +2 -0
- package/dist/types/server/src/shared/json.d.ts +25 -0
- package/dist/types/server/src/shared/market-codes.d.ts +16 -0
- package/dist/types/src/account/addresses-view.d.ts +5 -0
- package/dist/types/src/account/answers.d.ts +40 -0
- package/dist/types/src/account/markup.d.ts +19 -0
- package/dist/types/src/account/operations.d.ts +41 -0
- package/dist/types/src/account/order-lines.d.ts +13 -0
- package/dist/types/src/account/order-list.d.ts +12 -0
- package/dist/types/src/account/order-page.d.ts +6 -0
- package/dist/types/src/account/page-answers.d.ts +182 -0
- package/dist/types/src/account/page-events.d.ts +2 -0
- package/dist/types/src/account/server-client.d.ts +60 -0
- package/dist/types/src/account/subscriptions-view.d.ts +2 -0
- package/dist/types/src/account-api.d.ts +18 -0
- package/dist/types/src/account-marks.d.ts +15 -0
- package/dist/types/src/account-pages.d.ts +8 -0
- package/dist/types/src/account.d.ts +64 -0
- package/dist/types/src/amounts.d.ts +12 -0
- package/dist/types/src/analytics-shopify.d.ts +41 -0
- package/dist/types/src/analytics.d.ts +2 -0
- package/dist/types/src/api-version.d.ts +28 -0
- package/dist/types/src/astro.d.ts +42 -0
- package/dist/types/src/cart/cart-shape.d.ts +43 -0
- package/dist/types/src/cart/checkout.d.ts +16 -0
- package/dist/types/src/cart/drawer.d.ts +18 -0
- package/dist/types/src/cart/mutations.d.ts +79 -0
- package/dist/types/src/cart/render.d.ts +17 -0
- package/dist/types/src/cart/store.d.ts +182 -0
- package/dist/types/src/cart.d.ts +106 -0
- package/dist/types/src/client.d.ts +100 -0
- package/dist/types/src/collection.d.ts +25 -0
- package/dist/types/src/config-sections.d.ts +40 -0
- package/dist/types/src/config.d.ts +185 -0
- package/dist/types/src/controls/cart-controls.d.ts +2 -0
- package/dist/types/src/controls/discount.d.ts +2 -0
- package/dist/types/src/controls/extras.d.ts +5 -0
- package/dist/types/src/controls/navigation.d.ts +12 -0
- package/dist/types/src/diagnose-markup.d.ts +19 -0
- package/dist/types/src/diagnose.d.ts +60 -0
- package/dist/types/src/dom.d.ts +75 -0
- package/dist/types/src/extras.d.ts +21 -0
- package/dist/types/src/fragments.d.ts +158 -0
- package/dist/types/src/globals.d.ts +15 -0
- package/dist/types/src/images.d.ts +20 -0
- package/dist/types/src/index.d.ts +167 -0
- package/dist/types/src/initial.d.ts +56 -0
- package/dist/types/src/lazy.d.ts +28 -0
- package/dist/types/src/loader.d.ts +84 -0
- package/dist/types/src/market-codes.d.ts +6 -0
- package/dist/types/src/markets-ui.d.ts +32 -0
- package/dist/types/src/markets.d.ts +72 -0
- package/dist/types/src/markup.d.ts +131 -0
- package/dist/types/src/mounting.d.ts +46 -0
- package/dist/types/src/page-market.d.ts +27 -0
- package/dist/types/src/presets/barba.d.ts +56 -0
- package/dist/types/src/product-events.d.ts +2 -0
- package/dist/types/src/product-render.d.ts +17 -0
- package/dist/types/src/product-shape.d.ts +88 -0
- package/dist/types/src/product-state.d.ts +110 -0
- package/dist/types/src/product.d.ts +66 -0
- package/dist/types/src/repeaters.d.ts +16 -0
- package/dist/types/src/search.d.ts +33 -0
- package/dist/types/src/selling-plans.d.ts +64 -0
- package/dist/types/src/server.d.ts +88 -0
- package/dist/types/src/shopify-values.d.ts +46 -0
- package/dist/types/src/status.d.ts +33 -0
- package/dist/types/src/storage.d.ts +12 -0
- package/dist/types/src/store-html.d.ts +39 -0
- package/dist/types/src/storefront/catalog.d.ts +38 -0
- package/dist/types/src/storefront/json-ld.d.ts +20 -0
- package/dist/types/src/variant-render.d.ts +14 -0
- package/package.json +24 -23
- package/src/account/addresses-view.ts +58 -0
- package/src/account/answers.ts +79 -0
- package/src/account/markup.ts +36 -0
- package/src/account/operations.ts +145 -0
- package/src/account/{order-lines.js → order-lines.ts} +20 -41
- package/src/account/order-list.ts +107 -0
- package/src/account/order-page.ts +137 -0
- package/src/account/page-answers.ts +317 -0
- package/src/account/{page-events.js → page-events.ts} +55 -90
- package/src/account/server-client.ts +183 -0
- package/src/account/{subscriptions-view.js → subscriptions-view.ts} +25 -68
- package/src/account-api.ts +98 -0
- package/src/account-marks.ts +35 -0
- package/src/{account-pages.js → account-pages.ts} +29 -37
- package/src/account.ts +284 -0
- package/src/amounts.ts +64 -0
- package/src/{analytics-shopify.js → analytics-shopify.ts} +112 -107
- package/src/analytics.ts +315 -0
- package/src/{api-version.js → api-version.ts} +25 -31
- package/src/{astro.js → astro.ts} +110 -104
- package/src/cart/cart-shape.ts +25 -0
- package/src/cart/checkout.ts +101 -0
- package/src/cart/{drawer.js → drawer.ts} +18 -31
- package/src/cart/mutations.ts +237 -0
- package/src/cart/{render.js → render.ts} +68 -96
- package/src/cart/store.ts +279 -0
- package/src/{cart.js → cart.ts} +82 -128
- package/src/client.ts +391 -0
- package/src/{collection.js → collection.ts} +30 -38
- package/src/config-sections.ts +284 -0
- package/src/config.ts +480 -0
- package/src/controls/{cart-controls.js → cart-controls.ts} +22 -54
- package/src/controls/{discount.js → discount.ts} +13 -34
- package/src/controls/{extras.js → extras.ts} +15 -24
- package/src/controls/{navigation.js → navigation.ts} +24 -30
- package/src/{diagnose-markup.js → diagnose-markup.ts} +37 -72
- package/src/{diagnose.js → diagnose.ts} +95 -96
- package/src/dom.ts +268 -0
- package/src/{extras.js → extras.ts} +43 -52
- package/src/fragments.ts +261 -0
- package/src/globals.d.ts +15 -0
- package/src/{images.js → images.ts} +30 -43
- package/src/index.ts +415 -0
- package/src/initial.ts +101 -0
- package/src/lazy-iife.ts +197 -0
- package/src/lazy.ts +42 -0
- package/src/{loader.js → loader.ts} +149 -123
- package/src/market-codes.ts +6 -0
- package/src/{markets-ui.js → markets-ui.ts} +91 -68
- package/src/markets.ts +246 -0
- package/src/markup.ts +127 -0
- package/src/{mounting.js → mounting.ts} +88 -101
- package/src/{page-market.js → page-market.ts} +32 -38
- package/src/part-integrity.ts +7 -0
- package/src/parts/account-pages.ts +7 -0
- package/src/parts/account.ts +7 -0
- package/src/parts/analytics.ts +7 -0
- package/src/parts/diagnose.ts +7 -0
- package/src/parts/extras.ts +7 -0
- package/src/parts/markets.ts +7 -0
- package/src/parts/search.ts +7 -0
- package/src/presets/barba.ts +194 -0
- package/src/{product-events.js → product-events.ts} +33 -48
- package/src/{product-render.js → product-render.ts} +38 -77
- package/src/product-shape.ts +49 -0
- package/src/product-state.ts +318 -0
- package/src/{product.js → product.ts} +90 -126
- package/src/{repeaters.js → repeaters.ts} +40 -52
- package/src/{search.js → search.ts} +85 -103
- package/src/selling-plans.ts +145 -0
- package/src/server.ts +211 -0
- package/src/{shopify-values.js → shopify-values.ts} +24 -22
- package/src/{status.js → status.ts} +34 -54
- package/src/storage.ts +34 -0
- package/src/store-html.ts +359 -0
- package/src/storefront/catalog.ts +196 -0
- package/src/storefront/{json-ld.js → json-ld.ts} +45 -68
- package/src/{variant-render.js → variant-render.ts} +24 -50
- package/dist/esm/chunks/account-C3NP6HFB.js +0 -1
- package/dist/esm/chunks/account-pages-WLK374FO.js +0 -1
- package/dist/esm/chunks/analytics-G3SAWMNL.js +0 -1
- package/dist/esm/chunks/chunk-27PJJZGC.js +0 -1
- package/dist/esm/chunks/chunk-2FSXS55U.js +0 -1
- package/dist/esm/chunks/chunk-FCSWQDVV.js +0 -1
- package/dist/esm/chunks/chunk-FOMSMIAM.js +0 -1
- package/dist/esm/chunks/chunk-GZJTCLLW.js +0 -1
- package/dist/esm/chunks/chunk-HEQILZVR.js +0 -1
- package/dist/esm/chunks/chunk-JOOEXYF3.js +0 -4
- package/dist/esm/chunks/chunk-MPTQYLSM.js +0 -1
- package/dist/esm/chunks/chunk-NEHZ37TU.js +0 -3
- package/dist/esm/chunks/chunk-V74KGEJF.js +0 -1
- package/dist/esm/chunks/chunk-VT4CDUJJ.js +0 -1
- package/dist/esm/chunks/chunk-YJLWX2HK.js +0 -1
- package/dist/esm/chunks/diagnose-6VVGZNTY.js +0 -14
- package/dist/esm/chunks/extras-GJVRDFM3.js +0 -1
- package/dist/esm/chunks/markets-ui-UNB4PTA7.js +0 -1
- package/dist/esm/chunks/search-OU75RNQL.js +0 -1
- package/src/account/addresses-view.js +0 -92
- package/src/account/browser-session.js +0 -359
- package/src/account/markup.js +0 -48
- package/src/account/operations.js +0 -180
- package/src/account/order-list.js +0 -91
- package/src/account/order-page.js +0 -192
- package/src/account/server-client.js +0 -172
- package/src/account/shared.js +0 -111
- package/src/account/shopify-auth.js +0 -150
- package/src/account-api.js +0 -118
- package/src/account-links.js +0 -35
- package/src/account.js +0 -342
- package/src/amounts.js +0 -36
- package/src/analytics.js +0 -352
- package/src/cart/checkout.js +0 -97
- package/src/cart/mutations.js +0 -234
- package/src/cart/store.js +0 -326
- package/src/client.js +0 -267
- package/src/config.js +0 -349
- package/src/dom.js +0 -287
- package/src/fragments.js +0 -237
- package/src/index.js +0 -353
- package/src/initial.js +0 -87
- package/src/lazy-iife.js +0 -116
- package/src/lazy.js +0 -39
- package/src/market-codes.js +0 -35
- package/src/markets.js +0 -334
- package/src/markup.js +0 -64
- package/src/parts/account-pages.js +0 -6
- package/src/parts/account.js +0 -6
- package/src/parts/analytics.js +0 -6
- package/src/parts/diagnose.js +0 -6
- package/src/parts/extras.js +0 -6
- package/src/parts/markets.js +0 -6
- package/src/parts/search.js +0 -6
- package/src/presets/barba.js +0 -190
- package/src/product-state.js +0 -318
- package/src/selling-plans.js +0 -154
- package/src/server.js +0 -215
- package/src/store-html.js +0 -0
- package/src/storefront/catalog.js +0 -199
- package/types/astro.d.ts +0 -28
- package/types/barba.d.ts +0 -16
- package/types/index.d.ts +0 -577
- package/types/server.d.ts +0 -182
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The catalog reads of `createStorefront()`: products, collections and recommendations, with the browser script's own
|
|
3
|
+
* product fragment, so what the server renders and hands over has the shape the script expects. Runs in Node and
|
|
4
|
+
* Workers: no DOM, no shared config.
|
|
5
|
+
*/
|
|
6
|
+
import { orNull, request, shaped } from '../client.ts';
|
|
7
|
+
import { productFragment, collectionFields } from '../fragments.ts';
|
|
8
|
+
import { productKey } from '../shopify-values.ts';
|
|
9
|
+
import { PRODUCT, PRODUCT_NODE } from '../product-shape.ts';
|
|
10
|
+
import { isCollectionAnswer, isRecommendationsAnswer, isSearchAnswer } from '../loader.ts';
|
|
11
|
+
import type { DataCheck, StorefrontConfig } from '../client.ts';
|
|
12
|
+
import type { ProductFragmentConfig, Product } from '../fragments.ts';
|
|
13
|
+
import type { Collection, PageInfo, ProductPage } from '../loader.ts';
|
|
14
|
+
|
|
15
|
+
/** What the catalog needs of a storefront's config. */
|
|
16
|
+
type CatalogConfig = StorefrontConfig & ProductFragmentConfig;
|
|
17
|
+
|
|
18
|
+
/** A page of a collection's products, or of a search. */
|
|
19
|
+
export interface CatalogQuery {
|
|
20
|
+
/** A collection handle; without one, a search. */
|
|
21
|
+
collection?: string;
|
|
22
|
+
/** Storefront search syntax; `null` or `''` is every product. */
|
|
23
|
+
query?: string | null;
|
|
24
|
+
/** Default the storefront's `pageSize`. */
|
|
25
|
+
first?: number;
|
|
26
|
+
/** The cursor of the page before. */
|
|
27
|
+
after?: string | null;
|
|
28
|
+
/** `ProductCollectionSortKeys` for a collection, `ProductSortKeys` for a search. */
|
|
29
|
+
sortKey?: string;
|
|
30
|
+
reverse?: boolean;
|
|
31
|
+
/** `ProductFilter` values, for a collection. */
|
|
32
|
+
filters?: unknown[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
type CollectionWithProducts = Collection & { products: Product[]; pageInfo: PageInfo };
|
|
36
|
+
type RecommendationIntent = 'RELATED' | 'COMPLEMENTARY';
|
|
37
|
+
|
|
38
|
+
/** A page of a GraphQL connection. */
|
|
39
|
+
interface Connection<Node> {
|
|
40
|
+
nodes: Node[];
|
|
41
|
+
pageInfo: PageInfo;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Stops a paging loop on a store that keeps answering `hasNextPage` (500 pages of 25 is 12,500 products). */
|
|
45
|
+
const MAX_PRODUCT_PAGES = 500;
|
|
46
|
+
const COLLECTIONS_PER_PAGE = 100;
|
|
47
|
+
const MAX_COLLECTION_PAGES = 100;
|
|
48
|
+
|
|
49
|
+
const PRODUCT_BY_ID = `query TetherServerProductById($id: ID!) { node(id: $id) { ... on Product { ...TetherProduct } } }`;
|
|
50
|
+
const PRODUCT_BY_HANDLE = `query TetherServerProduct($handle: String!) { product(handle: $handle) { ...TetherProduct } }`;
|
|
51
|
+
|
|
52
|
+
const collectionQuery =
|
|
53
|
+
() => `query TetherServerCollection($handle: String!, $first: Int!, $after: String, $sortKey: ProductCollectionSortKeys, $reverse: Boolean, $filters: [ProductFilter!]) {
|
|
54
|
+
collection(handle: $handle) {
|
|
55
|
+
${collectionFields()}
|
|
56
|
+
products(first: $first, after: $after, sortKey: $sortKey, reverse: $reverse, filters: $filters) {
|
|
57
|
+
nodes { ...TetherProduct }
|
|
58
|
+
pageInfo { hasNextPage endCursor }
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}`;
|
|
62
|
+
|
|
63
|
+
const SEARCH_QUERY = `query TetherServerProducts($first: Int!, $after: String, $query: String, $sortKey: ProductSortKeys, $reverse: Boolean) {
|
|
64
|
+
products(first: $first, after: $after, query: $query, sortKey: $sortKey, reverse: $reverse) {
|
|
65
|
+
nodes { ...TetherProduct }
|
|
66
|
+
pageInfo { hasNextPage endCursor }
|
|
67
|
+
}
|
|
68
|
+
}`;
|
|
69
|
+
|
|
70
|
+
const collectionsQuery = () => `query TetherServerCollections($first: Int!, $after: String) {
|
|
71
|
+
collections(first: $first, after: $after) { nodes { ${collectionFields()} } pageInfo { hasNextPage endCursor } }
|
|
72
|
+
}`;
|
|
73
|
+
|
|
74
|
+
const recommendationsQuery = (
|
|
75
|
+
byId: boolean,
|
|
76
|
+
) => `query TetherServerRecs(${byId ? '$id: ID!' : '$handle: String!'}, $intent: ProductRecommendationIntent) {
|
|
77
|
+
productRecommendations(${byId ? 'productId: $id' : 'productHandle: $handle'}, intent: $intent) { ...TetherProduct }
|
|
78
|
+
}`;
|
|
79
|
+
|
|
80
|
+
const isProductAnswer = shaped<{ product: Product | null }>({ product: orNull(PRODUCT) });
|
|
81
|
+
const isNodeAnswer = shaped<{ node: Product | { id?: undefined } | null }>({ node: orNull(PRODUCT_NODE) });
|
|
82
|
+
const isCollectionsAnswer = shaped<{ collections: Connection<Collection> }>({
|
|
83
|
+
collections: { nodes: [{ handle: 'string' }], pageInfo: { hasNextPage: 'boolean' } },
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
async function allCollectionsOf(conf: StorefrontConfig): Promise<Collection[]> {
|
|
87
|
+
const collections: Collection[] = [];
|
|
88
|
+
let after: string | null = null;
|
|
89
|
+
for (let page = 0; page < MAX_COLLECTION_PAGES; page++) {
|
|
90
|
+
const data: { collections: Connection<Collection> } = await request(
|
|
91
|
+
conf,
|
|
92
|
+
collectionsQuery(),
|
|
93
|
+
{ first: COLLECTIONS_PER_PAGE, after },
|
|
94
|
+
isCollectionsAnswer,
|
|
95
|
+
);
|
|
96
|
+
collections.push(...data.collections.nodes);
|
|
97
|
+
if (!data.collections.pageInfo.hasNextPage) break;
|
|
98
|
+
after = data.collections.pageInfo.endCursor;
|
|
99
|
+
}
|
|
100
|
+
return collections;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** @param pageSize Products per page when a query doesn't say. */
|
|
104
|
+
export function createCatalog(conf: CatalogConfig, pageSize: number) {
|
|
105
|
+
/** @param operation The query, without the product fragment. */
|
|
106
|
+
const send = <T>(operation: string, variables: Record<string, unknown>, isData: DataCheck<T>): Promise<T> =>
|
|
107
|
+
request(conf, (access) => `${operation}\n${productFragment(conf, access)}`, variables, isData);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* @param raw A handle, numeric id, gid or product URL.
|
|
111
|
+
* @returns `null` when the store has no such product.
|
|
112
|
+
*/
|
|
113
|
+
async function getProduct(raw: string | number | null | undefined): Promise<Product | null> {
|
|
114
|
+
const key = productKey(raw);
|
|
115
|
+
if (!key) return null;
|
|
116
|
+
if (key.startsWith('gid://')) {
|
|
117
|
+
const { node } = await send(PRODUCT_BY_ID, { id: key }, isNodeAnswer);
|
|
118
|
+
return node?.id ? node : null;
|
|
119
|
+
}
|
|
120
|
+
const data = await send(PRODUCT_BY_HANDLE, { handle: key }, isProductAnswer);
|
|
121
|
+
return data.product;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** @returns No collection and no products for a handle the store doesn't have. */
|
|
125
|
+
async function getCollectionPage(
|
|
126
|
+
handle: string,
|
|
127
|
+
{ first = pageSize, after = null, sortKey, reverse = false, filters }: Omit<CatalogQuery, 'collection' | 'query'>,
|
|
128
|
+
): Promise<ProductPage> {
|
|
129
|
+
const data = await send(
|
|
130
|
+
collectionQuery(),
|
|
131
|
+
{
|
|
132
|
+
handle,
|
|
133
|
+
first,
|
|
134
|
+
after,
|
|
135
|
+
sortKey: sortKey || undefined,
|
|
136
|
+
reverse,
|
|
137
|
+
filters: filters ?? undefined,
|
|
138
|
+
},
|
|
139
|
+
isCollectionAnswer,
|
|
140
|
+
);
|
|
141
|
+
if (!data.collection) return { collection: null, products: [], pageInfo: { hasNextPage: false, endCursor: null } };
|
|
142
|
+
const { products, ...collection } = data.collection;
|
|
143
|
+
return { collection, products: products.nodes, pageInfo: products.pageInfo };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async function getProducts(options: CatalogQuery = {}): Promise<ProductPage> {
|
|
147
|
+
const { collection, query, first = pageSize, after = null, sortKey, reverse = false } = options;
|
|
148
|
+
if (collection) return getCollectionPage(collection, options);
|
|
149
|
+
const data = await send(
|
|
150
|
+
SEARCH_QUERY,
|
|
151
|
+
{ first, after, query: query || undefined, sortKey: sortKey || undefined, reverse },
|
|
152
|
+
isSearchAnswer,
|
|
153
|
+
);
|
|
154
|
+
return { collection: null, products: data.products.nodes, pageInfo: data.products.pageInfo };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Every product of a collection or a search, page by page: for `getStaticPaths()`. */
|
|
158
|
+
async function allProducts(options: Omit<CatalogQuery, 'first' | 'after'> = {}): Promise<Product[]> {
|
|
159
|
+
const products: Product[] = [];
|
|
160
|
+
let after: string | null = null;
|
|
161
|
+
for (let page = 0; page < MAX_PRODUCT_PAGES; page++) {
|
|
162
|
+
const result = await getProducts({ ...options, first: pageSize, after });
|
|
163
|
+
products.push(...result.products);
|
|
164
|
+
if (!result.pageInfo.hasNextPage) break;
|
|
165
|
+
after = result.pageInfo.endCursor;
|
|
166
|
+
}
|
|
167
|
+
return products;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** @returns `null` for a handle the store doesn't have. */
|
|
171
|
+
async function getCollection(
|
|
172
|
+
handle: string,
|
|
173
|
+
options: Omit<CatalogQuery, 'collection'> = {},
|
|
174
|
+
): Promise<CollectionWithProducts | null> {
|
|
175
|
+
const page = await getProducts({ ...options, collection: handle });
|
|
176
|
+
return page.collection ? { ...page.collection, products: page.products, pageInfo: page.pageInfo } : null;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** By product id (gid or numeric) or handle, so it can run next to `getProduct(handle)`. */
|
|
180
|
+
async function getRecommendations(
|
|
181
|
+
product: string | number | { id: string } | null | undefined,
|
|
182
|
+
intent: RecommendationIntent = 'RELATED',
|
|
183
|
+
): Promise<Product[]> {
|
|
184
|
+
const key = productKey(typeof product === 'object' ? product?.id : product);
|
|
185
|
+
const byId = key?.startsWith('gid://') ?? false;
|
|
186
|
+
const data = await send(
|
|
187
|
+
recommendationsQuery(byId),
|
|
188
|
+
byId ? { id: key, intent } : { handle: key, intent },
|
|
189
|
+
isRecommendationsAnswer,
|
|
190
|
+
);
|
|
191
|
+
return data.productRecommendations ?? [];
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const allCollections = () => allCollectionsOf(conf);
|
|
195
|
+
return { getProduct, getProducts, allProducts, getCollection, allCollections, getRecommendations };
|
|
196
|
+
}
|
|
@@ -3,22 +3,26 @@
|
|
|
3
3
|
* `Product` per variant, each with its price, currency, stock, sku, image and `?variant=` link. Products that are only
|
|
4
4
|
* sold on a subscription are priced at their first plan.
|
|
5
5
|
*/
|
|
6
|
-
import { allPlans, allocationFor, planPrice } from '../selling-plans.
|
|
7
|
-
import { productUrl } from '../shopify-values.
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
import { allPlans, allocationFor, planPrice } from '../selling-plans.ts';
|
|
7
|
+
import { productUrl } from '../shopify-values.ts';
|
|
8
|
+
import type { Product, ProductVariant } from '../fragments.ts';
|
|
9
|
+
import type { Money } from '../shopify-values.ts';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
export interface JsonLdOptions {
|
|
12
|
+
/** The product page's absolute URL; default from the storefront's `siteUrl`. */
|
|
13
|
+
url?: string;
|
|
14
|
+
/** Default the product's vendor. */
|
|
15
|
+
brand?: string;
|
|
16
|
+
}
|
|
16
17
|
|
|
17
|
-
/**
|
|
18
|
+
/** Where the product pages are. */
|
|
19
|
+
interface JsonLdSite {
|
|
20
|
+
productPath: string;
|
|
21
|
+
siteUrl: string | undefined;
|
|
22
|
+
}
|
|
18
23
|
|
|
19
24
|
/** The option names schema.org has a property for: a `ProductGroup` varies by them. */
|
|
20
|
-
|
|
21
|
-
const VARIES_BY = {
|
|
25
|
+
const VARIES_BY: Readonly<Record<string, string>> = {
|
|
22
26
|
size: 'https://schema.org/size',
|
|
23
27
|
color: 'https://schema.org/color',
|
|
24
28
|
colour: 'https://schema.org/color',
|
|
@@ -28,56 +32,36 @@ const VARIES_BY = {
|
|
|
28
32
|
/**
|
|
29
33
|
* Text for a `<script>` tag: `<` is escaped so product text can never close the tag, and U+2028 / U+2029 so older
|
|
30
34
|
* JavaScript parsers read it.
|
|
31
|
-
*
|
|
32
|
-
* @param {unknown} value
|
|
33
|
-
* @returns {string}
|
|
34
35
|
*/
|
|
35
|
-
export const scriptJson = (value) =>
|
|
36
|
+
export const scriptJson = (value: unknown): string =>
|
|
36
37
|
JSON.stringify(value)
|
|
37
38
|
.replace(/</g, '\\u003c')
|
|
38
39
|
.replace(/\u2028/g, '\\u2028')
|
|
39
40
|
.replace(/\u2029/g, '\\u2029');
|
|
40
41
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
*/
|
|
45
|
-
function withoutUndefined(value) {
|
|
46
|
-
/** @type {unknown} */
|
|
47
|
-
const copy = JSON.parse(JSON.stringify(value));
|
|
42
|
+
/** @returns `value` without its `undefined` fields, nested ones too. */
|
|
43
|
+
function withoutUndefined(value: Record<string, unknown>): Record<string, unknown> {
|
|
44
|
+
const copy: unknown = JSON.parse(JSON.stringify(value));
|
|
48
45
|
// The JSON of an object is an object.
|
|
49
|
-
return
|
|
46
|
+
return copy as Record<string, unknown>;
|
|
50
47
|
}
|
|
51
48
|
|
|
52
|
-
|
|
53
|
-
const numericId = (gid) => gid.split('/').pop() ?? gid;
|
|
49
|
+
const numericId = (gid: string) => gid.split('/').pop() ?? gid;
|
|
54
50
|
|
|
55
|
-
/**
|
|
56
|
-
|
|
57
|
-
* @returns {string} The schema.org availability.
|
|
58
|
-
*/
|
|
59
|
-
function availability(variant) {
|
|
51
|
+
/** @returns The schema.org availability. */
|
|
52
|
+
function availability(variant: ProductVariant): string {
|
|
60
53
|
if (!variant.availableForSale) return 'https://schema.org/OutOfStock';
|
|
61
54
|
return variant.currentlyNotInStock ? 'https://schema.org/BackOrder' : 'https://schema.org/InStock';
|
|
62
55
|
}
|
|
63
56
|
|
|
64
|
-
/**
|
|
65
|
-
|
|
66
|
-
* @param {ProductVariant} variant
|
|
67
|
-
* @returns {Money} The variant's price, or its first plan's for a product sold only on a subscription.
|
|
68
|
-
*/
|
|
69
|
-
function offeredPrice(product, variant) {
|
|
57
|
+
/** @returns The variant's price, or its first plan's for a product sold only on a subscription. */
|
|
58
|
+
function offeredPrice(product: Product, variant: ProductVariant): Money {
|
|
70
59
|
if (!product.requiresSellingPlan) return variant.price;
|
|
71
60
|
const plan = allPlans(product).find((candidate) => allocationFor(variant, candidate.id));
|
|
72
61
|
return (plan && planPrice(variant, plan.id)) ?? variant.price;
|
|
73
62
|
}
|
|
74
63
|
|
|
75
|
-
|
|
76
|
-
* @param {Product} product
|
|
77
|
-
* @param {ProductVariant} variant
|
|
78
|
-
* @param {string | undefined} pageUrl
|
|
79
|
-
*/
|
|
80
|
-
function offer(product, variant, pageUrl) {
|
|
64
|
+
function offer(product: Product, variant: ProductVariant, pageUrl: string | undefined) {
|
|
81
65
|
const price = offeredPrice(product, variant);
|
|
82
66
|
const separator = pageUrl?.includes('?') ? '&' : '?';
|
|
83
67
|
return {
|
|
@@ -89,15 +73,14 @@ function offer(product, variant, pageUrl) {
|
|
|
89
73
|
};
|
|
90
74
|
}
|
|
91
75
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
const properties = variant.selectedOptions.flatMap((option) => {
|
|
76
|
+
function variantProduct(
|
|
77
|
+
product: Product,
|
|
78
|
+
variant: ProductVariant,
|
|
79
|
+
pageUrl: string | undefined,
|
|
80
|
+
images: readonly string[],
|
|
81
|
+
) {
|
|
82
|
+
// schema.org property name → the variant's value.
|
|
83
|
+
const properties = variant.selectedOptions.flatMap((option): [string, string][] => {
|
|
101
84
|
const property = VARIES_BY[option.name.toLowerCase()];
|
|
102
85
|
return property ? [[property.split('/').pop() ?? property, option.value]] : [];
|
|
103
86
|
});
|
|
@@ -111,25 +94,19 @@ function variantProduct(product, variant, pageUrl, images) {
|
|
|
111
94
|
};
|
|
112
95
|
}
|
|
113
96
|
|
|
114
|
-
|
|
115
|
-
* @param {Product} product
|
|
116
|
-
* @param {JsonLdOptions} options
|
|
117
|
-
* @param {JsonLdSite} site
|
|
118
|
-
* @returns {string | undefined}
|
|
119
|
-
*/
|
|
120
|
-
function pageUrlOf(product, { url }, site) {
|
|
97
|
+
function pageUrlOf(product: Product, { url }: JsonLdOptions, site: JsonLdSite): string | undefined {
|
|
121
98
|
if (url) return url;
|
|
122
|
-
|
|
123
|
-
|
|
99
|
+
const path = productUrl(site.productPath, product.handle, product.id);
|
|
100
|
+
if (!site.siteUrl || !path) return undefined;
|
|
101
|
+
return new URL(path, site.siteUrl).toString();
|
|
124
102
|
}
|
|
125
103
|
|
|
126
|
-
/**
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
export function jsonLdData(product, options, site) {
|
|
104
|
+
/** @returns The schema.org object, `null` without a product. */
|
|
105
|
+
export function jsonLdData(
|
|
106
|
+
product: Product | null | undefined,
|
|
107
|
+
options: JsonLdOptions,
|
|
108
|
+
site: JsonLdSite,
|
|
109
|
+
): Record<string, unknown> | null {
|
|
133
110
|
if (!product) return null;
|
|
134
111
|
const pageUrl = pageUrlOf(product, options, site);
|
|
135
112
|
const images = product.images.nodes.map((image) => image.url).filter(Boolean);
|
|
@@ -1,45 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The DOM writes of a product scope that follow the visitor's choice, on every change: `product-state.
|
|
2
|
+
* The DOM writes of a product scope that follow the visitor's choice, on every change: `product-state.ts`'s view as
|
|
3
3
|
* text, selected and unavailable states, `tether-show` conditions and the add-to-cart buttons.
|
|
4
4
|
*/
|
|
5
|
-
import { attr, sel, get, qsa, formatMoney, formatPercent, setText, setImage, setSelected, show } from './dom.
|
|
6
|
-
import { STATUS_KEYS, ownedBy } from './status.
|
|
7
|
-
import { allocationFor } from './selling-plans.
|
|
8
|
-
import { isValueAvailable, planBindTexts, variantBindTexts } from './product-state.
|
|
9
|
-
import { isSelect, selectsIn } from './product-render.
|
|
10
|
-
|
|
11
|
-
|
|
5
|
+
import { attr, sel, get, qsa, formatMoney, formatPercent, setText, setImage, setSelected, show } from './dom.ts';
|
|
6
|
+
import { STATUS_KEYS, ownedBy } from './status.ts';
|
|
7
|
+
import { allocationFor } from './selling-plans.ts';
|
|
8
|
+
import { isValueAvailable, planBindTexts, variantBindTexts } from './product-state.ts';
|
|
9
|
+
import { isSelect, selectsIn } from './product-render.ts';
|
|
10
|
+
import type { Formatters, ProductView } from './product-state.ts';
|
|
11
|
+
import type { RenderScope } from './product-render.ts';
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
const FORMAT = { money: formatMoney, percent: formatPercent };
|
|
13
|
+
const FORMAT: Formatters = { money: formatMoney, percent: formatPercent };
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
* @param {Element} el
|
|
18
|
-
* @returns {el is HTMLInputElement}
|
|
19
|
-
*/
|
|
20
|
-
const isInput = (el) => el.tagName === 'INPUT';
|
|
15
|
+
const isInput = (el: Element): el is HTMLInputElement => el.tagName === 'INPUT';
|
|
21
16
|
|
|
22
17
|
/**
|
|
23
18
|
* The element of an add-to-cart button that holds its label, with the label as written kept in
|
|
24
19
|
* `data-tether-label`, so mode-dependent texts (`tether-subscribe-text`, `tether-soldout-text`) can be undone.
|
|
25
|
-
*
|
|
26
|
-
* @param {HTMLElement} button
|
|
27
|
-
* @returns {{ label: HTMLElement, original: string }}
|
|
28
20
|
*/
|
|
29
|
-
export function addButtonLabel(button) {
|
|
21
|
+
export function addButtonLabel(button: HTMLElement): { label: HTMLElement; original: string } {
|
|
30
22
|
const label = qsa(button, sel('add-label'))[0] ?? button;
|
|
31
23
|
const original = label.dataset['tetherLabel'] ?? (isInput(label) ? label.value : label.textContent);
|
|
32
24
|
label.dataset['tetherLabel'] = original;
|
|
33
25
|
return { label, original };
|
|
34
26
|
}
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
* @param {RenderScope} scope
|
|
38
|
-
* @param {ProductView} view
|
|
39
|
-
*/
|
|
40
|
-
function renderOptionStates(scope, view) {
|
|
28
|
+
function renderOptionStates(scope: RenderScope, view: ProductView): void {
|
|
41
29
|
const { product } = scope;
|
|
42
|
-
const selectedValue = (
|
|
30
|
+
const selectedValue = (authored: string | null) => view.selected[scope.optName(authored)] ?? '';
|
|
43
31
|
scope.$(sel('option-selected')).forEach((el) => {
|
|
44
32
|
setText(el, selectedValue(get(el, 'option-selected')));
|
|
45
33
|
});
|
|
@@ -63,12 +51,9 @@ function renderOptionStates(scope, view) {
|
|
|
63
51
|
|
|
64
52
|
/**
|
|
65
53
|
* The purchase toggles and the plan list and select.
|
|
66
|
-
*
|
|
67
|
-
* @param {RenderScope} scope
|
|
68
|
-
* @param {ProductView} view
|
|
69
54
|
*/
|
|
70
|
-
function renderPurchaseStates(scope, view) {
|
|
71
|
-
const { variant, purchase,
|
|
55
|
+
function renderPurchaseStates(scope: RenderScope, view: ProductView): void {
|
|
56
|
+
const { variant, purchase, line } = view;
|
|
72
57
|
scope.$(sel('purchase')).forEach((el) => {
|
|
73
58
|
if (isSelect(el)) {
|
|
74
59
|
el.value = purchase.kind;
|
|
@@ -81,7 +66,7 @@ function renderPurchaseStates(scope, view) {
|
|
|
81
66
|
});
|
|
82
67
|
scope.$(sel('plan-value')).forEach((node) => {
|
|
83
68
|
const planId = get(node, 'plan-value') ?? '';
|
|
84
|
-
setSelected(node,
|
|
69
|
+
setSelected(node, line.kind === 'subscription' && planId === line.planId);
|
|
85
70
|
node.classList.toggle('is-unavailable', !allocationFor(variant, planId));
|
|
86
71
|
const plan = scope.plans.find((candidate) => candidate.id === planId);
|
|
87
72
|
const texts = planBindTexts(variant, planId, plan, FORMAT);
|
|
@@ -91,19 +76,15 @@ function renderPurchaseStates(scope, view) {
|
|
|
91
76
|
});
|
|
92
77
|
});
|
|
93
78
|
selectsIn(scope, sel('plan')).forEach((select) => {
|
|
94
|
-
select.value =
|
|
79
|
+
select.value = line.kind === 'subscription' ? line.planId : '';
|
|
95
80
|
});
|
|
96
81
|
}
|
|
97
82
|
|
|
98
83
|
/**
|
|
99
84
|
* The `tether-show` conditions, with the scope's own status: a product scope that renders is ready.
|
|
100
|
-
*
|
|
101
|
-
* @param {RenderScope} scope
|
|
102
|
-
* @param {ProductView} view
|
|
103
85
|
*/
|
|
104
|
-
function renderConditions(scope, view) {
|
|
105
|
-
|
|
106
|
-
const conditions = new Map(Object.entries(view.show));
|
|
86
|
+
function renderConditions(scope: RenderScope, view: ProductView): void {
|
|
87
|
+
const conditions = new Map<string, boolean>(Object.entries(view.show));
|
|
107
88
|
STATUS_KEYS.forEach((status) => conditions.set(status, status === 'ready'));
|
|
108
89
|
scope.$(sel('show')).forEach((el) => {
|
|
109
90
|
const key = get(el, 'show') ?? '';
|
|
@@ -115,11 +96,7 @@ function renderConditions(scope, view) {
|
|
|
115
96
|
});
|
|
116
97
|
}
|
|
117
98
|
|
|
118
|
-
|
|
119
|
-
* @param {RenderScope} scope
|
|
120
|
-
* @param {ProductView} view
|
|
121
|
-
*/
|
|
122
|
-
function renderAddButtons(scope, view) {
|
|
99
|
+
function renderAddButtons(scope: RenderScope, view: ProductView): void {
|
|
123
100
|
const disabled = !view.variant.availableForSale;
|
|
124
101
|
scope.$(sel('add-to-cart')).forEach((button) => {
|
|
125
102
|
button.classList.toggle('is-disabled', disabled);
|
|
@@ -131,7 +108,7 @@ function renderAddButtons(scope, view) {
|
|
|
131
108
|
if (!soldOutText && !subscribeText) return;
|
|
132
109
|
const { label, original } = addButtonLabel(button);
|
|
133
110
|
let text = original;
|
|
134
|
-
if (view.
|
|
111
|
+
if (view.line.kind === 'subscription' && subscribeText) text = subscribeText;
|
|
135
112
|
if (disabled && soldOutText) text = soldOutText;
|
|
136
113
|
if (button.getAttribute('aria-busy') !== 'true') setText(label, text);
|
|
137
114
|
});
|
|
@@ -139,11 +116,8 @@ function renderAddButtons(scope, view) {
|
|
|
139
116
|
|
|
140
117
|
/**
|
|
141
118
|
* Writes what follows the variant and purchase.
|
|
142
|
-
*
|
|
143
|
-
* @param {RenderScope} scope
|
|
144
|
-
* @param {ProductView} view
|
|
145
119
|
*/
|
|
146
|
-
export function writeVariant(scope, view) {
|
|
120
|
+
export function writeVariant(scope: RenderScope, view: ProductView): void {
|
|
147
121
|
const { variant } = view;
|
|
148
122
|
const { product } = scope;
|
|
149
123
|
const texts = variantBindTexts(view, FORMAT);
|
|
@@ -159,7 +133,7 @@ export function writeVariant(scope, view) {
|
|
|
159
133
|
renderOptionStates(scope, view);
|
|
160
134
|
renderPurchaseStates(scope, view);
|
|
161
135
|
renderConditions(scope, view);
|
|
162
|
-
scope.root.classList.toggle('is-subscription', view.
|
|
163
|
-
scope.root.setAttribute(attr('purchase-mode'), view.
|
|
136
|
+
scope.root.classList.toggle('is-subscription', view.line.kind === 'subscription');
|
|
137
|
+
scope.root.setAttribute(attr('purchase-mode'), view.line.kind);
|
|
164
138
|
renderAddButtons(scope, view);
|
|
165
139
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as oe,b as se,c as F,g as R,h as ie}from"./chunk-GZJTCLLW.js";import{a as ae}from"./chunk-VT4CDUJJ.js";import{b as f}from"./chunk-MPTQYLSM.js";import"./chunk-NEHZ37TU.js";import"./chunk-JOOEXYF3.js";import{A as te,C as re,G as ne,J as N,L as K,b as i,g as ee,i as p,l as d,s as w,t as k,v as x,x as E}from"./chunk-YJLWX2HK.js";import"./chunk-FOMSMIAM.js";var ve="amount currencyCode",ce="query TetherCustomer { customer { id firstName lastName displayName emailAddress { emailAddress } defaultAddress { formatted } } }",ue=`query TetherOrders($first:Int!){customer{orders(first:$first,sortKey:PROCESSED_AT,reverse:true){nodes{id name processedAt financialStatus fulfillmentStatus statusPageUrl totalPrice{${ve}}lineItems(first:20){nodes{title variantTitle quantity image{url altText}}}}}}}`;function G(e){return btoa(String.fromCharCode(...new Uint8Array(e))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")}function le(e){let t=e.replace(/-/g,"+").replace(/_/g,"/").padEnd(Math.ceil(e.length/4)*4,"=");return Uint8Array.from(atob(t),r=>r.charCodeAt(0))}function P(e=32){return G(crypto.getRandomValues(new Uint8Array(e)))}var $=()=>`${i.prefix}:account:${i.domain}`,v=()=>`${i.prefix}:account-login:${i.domain}`,De=()=>`${i.prefix}:account-hint:${i.domain}`,D=()=>i.account?.remember?"local":"session",z=e=>e==="local"?window.localStorage:window.sessionStorage;function Q(e,t){try{return JSON.parse(z(e).getItem(t)??"null")}catch{return null}}function A(e,t,r){try{r===null?z(e).removeItem(t):z(e).setItem(t,JSON.stringify(r))}catch{}}function Y(e){A("local",De(),e?1:null)}var h=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),u=e=>typeof e=="string"?e:null;async function b(e){let t;try{t=await e.json()}catch{return{}}return h(t)?t:{}}function S(e){let t=i.account?.navigate;t?t(e):window.location.assign(e)}var q=e=>e||i.account?.locale||(i.locale||"").split("-")[0]||"",j=()=>i.country?i.country.toUpperCase():"";var qe=3600,B=null;async function de(e){let t=i.account?.shopDomain||i.domain,r=await fetch(`https://${t}/.well-known/${e}`);if(!r.ok)throw new d(`Customer accounts: ${e} answered ${r.status}`,{status:r.status});let n=await r.json();return h(n)?n:{}}function I(e,t){let r=u(e[t]);if(!r)throw new d(`Customer accounts: Shopify named no ${t}`);return r}async function C(){if(B)return B;let[e,t]=await Promise.all([de("openid-configuration"),de("customer-account-api")]);return B={issuer:I(e,"issuer"),authorizationEndpoint:I(e,"authorization_endpoint"),tokenEndpoint:I(e,"token_endpoint"),endSessionEndpoint:I(e,"end_session_endpoint"),graphql:I(t,"graphql_api")},B}async function H(e){let{tokenEndpoint:t}=await C(),r=await fetch(i.account?.tokenUrl||t,{method:"POST",body:new URLSearchParams(e)}),n=await b(r),o=u(n.access_token);if(!r.ok||!o){let s=r.status===400||r.status===401;throw new d(`Customer accounts: token ${r.status} ${u(n.error)??""}`.trim(),{status:r.status,code:s?"SESSION_ENDED":"TOKEN_FAILED"})}return{access:o,refresh:u(n.refresh_token)||null,idToken:u(n.id_token)||null,expiresInSeconds:Number(n.expires_in)||qe}}function je(e){let t=e.split(".")[1];if(!t)return null;let r;try{r=JSON.parse(new TextDecoder().decode(le(t)))}catch{return null}return h(r)?r:null}function fe(e,{nonce:t,issuer:r,client:n}){let o=e?je(e):null;if(!o)return!1;let s=o.aud,a=Array.isArray(s)?s:[s],c=o.exp;return o.nonce===t&&o.iss===r&&a.includes(n)&&typeof c=="number"&&c*1e3>=Date.now()}var Be=120,Je=600*1e3,l=null,O=null,V=null;function Me(e){if(!h(e))return null;let t=e.access,r=e.expiresAt;return typeof t!="string"||typeof r!="number"?null:{access:t,expiresAt:r,refresh:u(e.refresh),idToken:u(e.idToken)}}function Ke(e){if(!h(e))return null;let{state:t,nonce:r,verifier:n,redirect:o,returnTo:s,at:a}=e;return typeof t!="string"||typeof r!="string"||typeof n!="string"||typeof o!="string"||typeof s!="string"||typeof a!="number"?null:{state:t,nonce:r,verifier:n,redirect:o,returnTo:s,at:a}}var me=()=>l??Me(Q(D(),$())),pe=()=>l?.access??null,W=()=>!!(l&&(l.refresh||l.access&&l.expiresAt>Date.now()));function X(){let e=i.account?.clientId;if(!e)throw new d("Customer accounts: set account.clientId",{code:"NOT_CONFIGURED"});return e}var Fe=async e=>G(await crypto.subtle.digest("SHA-256",new TextEncoder().encode(e)));async function ge({returnTo:e,locale:t}){let r=X(),n=await C(),o={state:P(),nonce:P(),verifier:P(),redirect:i.account?.callbackUrl||`${window.location.origin}${window.location.pathname}`,returnTo:e,at:Date.now()};A("session",v(),o);let s=new URL(n.authorizationEndpoint),a={client_id:r,scope:"openid email customer-account-api:full",response_type:"code",redirect_uri:o.redirect,state:o.state,nonce:o.nonce,code_challenge:await Fe(o.verifier),code_challenge_method:"S256"},c=q(t);c&&(a.locale=c);let m=j();m&&(a.region_country=m),Object.entries(a).forEach(([_,$e])=>{s.searchParams.set(_,$e)}),S(s.toString())}function we(e,t){l={access:e.access,expiresAt:Date.now()+Math.max(0,e.expiresInSeconds-Be)*1e3,refresh:e.refresh??t?.refresh??null,idToken:e.idToken??t?.idToken??null},A(D(),$(),l)}async function he(){let e=new URLSearchParams(window.location.search),t=Ke(Q("session",v()));if(!t||!e.has("code")&&!e.has("error")||e.get("state")!==t.state)return!1;A("session",v(),null);let r=new URL(window.location.href);["code","state","iss","error","error_description"].forEach(m=>{r.searchParams.delete(m)}),window.history.replaceState(window.history.state,"",r);let n=e.get("error");if(n!==null)return E("account-error",{error:n}),!1;if(Date.now()-t.at>Je)throw new d("Customer accounts: the login took too long, please try again");let o=X(),s=await H({grant_type:"authorization_code",client_id:o,redirect_uri:t.redirect,code:e.get("code")??"",code_verifier:t.verifier}),{issuer:a}=await C();if(!fe(s.idToken,{nonce:t.nonce,issuer:a,client:o}))throw new d("Customer accounts: the login answer did not check out (nonce/issuer/audience)");we(s,null),await ye(),E("account-login",{});let c=t.returnTo?new URL(t.returnTo,window.location.href):null;return c?.origin===window.location.origin&&c.href!==r.href&&S(c.href),!0}function Z(){l=null,O=null,A(D(),$(),null),f.setCustomer({accessToken:null})}async function Ge(e){try{let t=await H({grant_type:"refresh_token",client_id:X(),refresh_token:e.refresh??""});we(t,e),await ye()}catch(t){if(t instanceof d&&t.code==="SESSION_ENDED")Z();else throw t}}async function U(){return l=me(),l?l.access&&l.expiresAt>Date.now()?l.access:l.refresh?(V??(V=Ge(l).finally(()=>{V=null})),await V,pe()):(Z(),null):null}async function ye(){let e=pe();if(f.setCustomer({accessToken:e}),!(!f.id()||!e))try{await f.setBuyerIdentity({customerAccessToken:e},{create:!1})}catch(t){p(t,"account cart")}}async function ze(){if(f.id())try{await f.setBuyerIdentity({customerAccessToken:null},{create:!1})}catch(e){p(e,"account cart unlink"),A("local",i.storageKey,null)}}async function xe(e){let t=me()?.idToken??null;if(await ze(),Z(),E("account-logout",{}),!t)return"stayed";let r=new URL((await C()).endSessionEndpoint);return r.searchParams.set("id_token_hint",t),r.searchParams.set("post_logout_redirect_uri",e),S(r.toString()),"redirected"}async function J(e,t={}){let r=await U();if(!r)throw new d("Not logged in",{code:"NOT_LOGGED_IN"});let{graphql:n}=await C(),o=m=>fetch(n,{method:"POST",headers:{"Content-Type":"application/json",Authorization:m},body:JSON.stringify({query:e,variables:t})}),s=await o(r);s.status===401&&l?.refresh&&(l.expiresAt=0,s=await o(await U()??""));let a=await b(s),c=Array.isArray(a.errors)?a.errors:[];if(!s.ok||c.length){let m=c.map(_=>h(_)?u(_.message):null).filter(Boolean);throw new d(m.join(" | ")||`Customer accounts: ${s.status}`,{status:s.status})}return a.data}async function ke(){return O||(O=(await J(ce)).customer,O)}function Ee(){O=null}var T=null;function g(){let e=i.account?.server;return e?new URL(e,window.location.href).href.replace(/\/$/,""):""}async function L(e,{method:t="GET",body:r}={}){let n={method:t,credentials:"include"};t==="POST"&&(n.headers={"Content-Type":"application/json","X-Tether-Account":"1"}),r&&(n.body=JSON.stringify(r));let o=await fetch(`${g()}${e}`,n);if(o.status===401)return{kind:"logged-out"};let s=await b(o);if(!o.ok){let a=u(s.error)??String(o.status),c=Array.isArray(s.userErrors)?s.userErrors:void 0;throw new d(`Customer accounts: ${a}`,{status:o.status,...c&&{userErrors:c}})}return{kind:"answer",data:s}}function Qe(e){let t=h(e)?e:{},r=t.address;return{firstName:u(t.firstName),lastName:u(t.lastName),displayName:u(t.displayName),email:u(t.email),address:Array.isArray(r)?r.filter(n=>typeof n=="string"):null}}async function M(){if(!T){let e=await L("/me");T=e.kind==="answer"&&e.data.loggedIn===!0?{loggedIn:!0,customer:Qe(e.data.customer)}:{loggedIn:!1}}return Y(T.loggedIn),T}var Ae=()=>T?.loggedIn===!0;function Se(){T=null}function Te({returnTo:e,locale:t}){let r=new URL(`${g()}/login`);r.searchParams.set("returnTo",e);let n=q(t);n&&r.searchParams.set("locale",n);let o=j();o&&r.searchParams.set("country",o),S(r.toString())}async function _e(){let e=f.id();if(!(await M()).loggedIn||!e)return null;let t=await L("/cart",{method:"POST",body:{cartId:e}});if(t.kind==="logged-out")return null;let r=u(t.data.checkoutUrl);return r?`${r}${r.includes("?")?"&":"?"}logged_in=true`:null}async function be(e){let t=await L("/logout",{method:"POST",body:{returnTo:e,cartId:f.id()}});T=null,Y(!1),E("account-logout",{});let r=t.kind==="answer"?u(t.data.logoutUrl):null;return r?(S(r),"redirected"):"stayed"}var Ce=10,Ye=e=>({name:e.name,date:oe(e.processedAt),total:ne(e.totalPrice),status:e.financialStatus?.toLowerCase(),fulfillment:e.fulfillmentStatus?.toLowerCase(),items:e.lineItems.nodes.map(t=>`${t.quantity}\xD7 ${t.title}`).join(", ")});function He(e,t){let r=t.lineItems.nodes,n=se(t),o=Ye(t);x(e,w("order-bind")).forEach(s=>{let a=k(s,"order-bind")??"";if(a==="url"){n&&s.setAttribute("href",n);return}if(a==="image"){let m=r.find(_=>_.image)?.image;m&&te(s,m.url,m.altText||r[0]?.title||"");return}let c=R(o,a);c!==void 0&&N(s,c)}),x(e,w("order-lines")).forEach(s=>{ie(s,r,null)}),k(e,"order-bind")==="url"&&n&&e.setAttribute("href",n)}async function Oe(e,t){let r=x(e,w("orders")),n=r[0];if(!n)return;F(e,"show",{"orders-loading":!0,"orders-empty":!1});let o=Number.parseInt(k(n,"limit")||String(Ce),10)||Ce,s=await t(o).catch(a=>(p(a,"orders"),null));F(e,"show",{"orders-loading":!1,"orders-empty":s?.length===0}),r.forEach(a=>{re(a,s??[],He)})}var Ve=["order","addresses","subscriptions","profile-form","address-form"];function We(){return g()?Ae():W()}async function Xe({returnTo:e=window.location.href,locale:t}={}){g()?Te({returnTo:e,locale:t}):await ge({returnTo:e,locale:t})}async function Ze(e,t={}){if(g())throw new d("Customer accounts in server mode only answer customer() and orders()",{code:"NOT_ALLOWED"});return J(e,t)}async function Ne(){if(!g())return ke();let e=await M();return e.loggedIn?{...e.customer,emailAddress:{emailAddress:e.customer.email},defaultAddress:{formatted:e.customer.address}}:null}async function et({first:e=10}={}){if(g()){let r=await L(`/orders?first=${e}`);if(r.kind==="logged-out")return[];let n=r.data.orders;return Array.isArray(n)?n:[]}return(await J(ue,{first:e})).customer?.orders.nodes??[]}async function tt({returnTo:e=i.account?.logoutUrl||`${window.location.origin}/`}={}){(g()?await be(e):await xe(e))==="stayed"&&await Re(document)}async function rt(){try{return g()?(await M()).loggedIn?{kind:"logged-in"}:{kind:"logged-out"}:W()||await U()?{kind:"logged-in"}:{kind:"logged-out"}}catch(e){return{kind:"unknown",error:e}}}var Le=e=>Ve.some(t=>x(e,w(t)).length>0);function nt(e){let t=i.account;x(e,w("account-link")).forEach(r=>{let n=k(r,"account-link"),o=t?.subscriptionsUrl,s=n==="payment"&&(t?.paymentUrl||o)||n==="subscriptions"&&o;r.setAttribute("href",s||`https://${i.domain}/account`)})}async function Re(e=document){let t=await rt();t.kind==="unknown"&&p(t.error,"account");let r=t.kind==="logged-in";if(x(e,w("show")).forEach(n=>{let o=k(n,"show");o==="logged-in"&&K(n,r),o==="logged-out"&&K(n,!r)}),nt(e),t.kind==="unknown"&&Le(e)){(await y()).showUnavailable(e);return}r&&(await Pe(e),await Promise.all([Oe(e,n=>et({first:n})),Le(e)?y().then(n=>n.renderPages(e)):null]))}async function Pe(e){let t=x(e,w("account-bind"));if(!t.length)return;let r=await Ne().catch(o=>(p(o,"account"),null)),n={"first-name":r?.firstName,"last-name":r?.lastName,name:r?.displayName,email:r?.emailAddress?.emailAddress,address:r?.defaultAddress?.formatted?.join(", ")};t.forEach(o=>{let s=R(n,k(o,"account-bind")??"");s!=null&&N(o,s)})}var Ie=null;function y(){return Ie??(Ie=ae("account-pages").then(e=>(e.setup({serverBase:g,serverFetch:L,query:Ze,customer:Ne,bindCustomer:Pe,reset:()=>{Ee(),Se()}}),e))),Ie}var Qt=e=>y().then(t=>t.order(e)),Yt=e=>y().then(t=>t.updateProfile(e)),Ht=()=>y().then(e=>e.addresses()),Vt=(e,t)=>y().then(r=>r.saveAddress(e,t)),Wt=e=>y().then(t=>t.deleteAddress(e)),Xt=()=>y().then(e=>e.subscriptions()),Zt=(e,t)=>y().then(r=>r.subscriptionAction(e,t)),er=(e,t)=>y().then(r=>r.requestReturn(e,t));function ot(e){let t=e.target;t instanceof window.Element&&(t.closest(w("account-login"))?(e.preventDefault(),Xe().catch(r=>{p(r,"login")})):t.closest(w("account-logout"))&&(e.preventDefault(),tt().catch(r=>{p(r,"logout")})))}function st(){let e=new URL(window.location.href);e.searchParams.get("account")&&(e.searchParams.delete("account"),window.history.replaceState(window.history.state,"",e),E("account-login",{})),f.setCustomer({beforeCheckout:_e})}async function it(){try{await he()}catch(e){p(e,"login")}if(!f.customerAccessToken)try{let e=await U();e&&f.setCustomer({accessToken:e})}catch(e){p(e,"account")}}var Ue=!1;async function tr(e=document){Ue||(Ue=!0,document.addEventListener("click",ot),g()?st():await it(),ee("account",We()?"logged in":"logged out")),await Re(e)}export{Ht as addresses,Ne as customer,Wt as deleteAddress,U as getToken,he as handleCallback,We as isLoggedIn,Xe as login,tt as logout,tr as mountAccount,Qt as order,et as orders,Ze as query,Re as render,er as requestReturn,Vt as saveAddress,Zt as subscriptionAction,Xt as subscriptions,Yt as updateProfile};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{a as C,c as f,d as O,e as E,f as N,g as w,h as oe}from"./chunk-GZJTCLLW.js";import{A as re,C as x,G as g,J as h,L as ne,i as I,l as D,r as y,s as a,t as m,v as l,x as se}from"./chunk-YJLWX2HK.js";import{c as z}from"./chunk-FOMSMIAM.js";var p=class extends Error{constructor(t,{status:s=400,userErrors:r=[]}={}){super(t),this.name="InputError",this.status=s,this.userErrors=r}},A="amount currencyCode",U="id firstName lastName company address1 address2 city province zoneCode zip country countryCode phoneNumber formatted",b="userErrors { field message }",_={order:`query TetherOrder($id:ID!){order(id:$id){id name number processedAt cancelledAt financialStatus fulfillmentStatus statusPageUrl requiresShipping totalPrice{${A}}subtotal{${A}}totalShipping{${A}}totalTax{${A}}shippingAddress{${U}}billingAddress{${U}}lineItems(first:50){nodes{id name title variantTitle quantity sku image{url altText}price{${A}}totalPrice{${A}}suggestedReturnReasonDefinitions(first:10){nodes{id name handle}}}}fulfillments(first:10){nodes{id status createdAt estimatedDeliveryAt latestShipmentStatus trackingInformation{company number url}}}returnInformation{returnableLineItems(first:50){nodes{quantity lineItem{id}}}}returns(first:10){nodes{id status}}}}`,addresses:`query TetherAddresses{customer{defaultAddress{id}addresses(first:20){nodes{${U}}}}}`,subscriptions:`query TetherSubscriptions{customer{subscriptionContracts(first:20,reverse:true){nodes{id status nextBillingDate currencyCode deliveryPrice{${A}}deliveryPolicy{interval intervalCount{count}}lines(first:20){nodes{id name title variantTitle quantity sku currentPrice{${A}}lineDiscountedPrice{${A}}image{url altText}}}upcomingBillingCycles(first:3){nodes{cycleIndex skipped billingAttemptExpectedDate}}}}}}`,contract:"query TetherContract($id:ID!){customer{subscriptionContract(id:$id){id status upcomingBillingCycles(first:3){nodes{cycleIndex skipped}}}}}"},L={profile:`mutation TetherProfile($input:CustomerUpdateInput!){customerUpdate(input:$input){customer{firstName lastName}${b}}}`,addressCreate:`mutation TetherAddressCreate($address:CustomerAddressInput!,$default:Boolean){customerAddressCreate(address:$address,defaultAddress:$default){customerAddress{${U}}${b}}}`,addressUpdate:`mutation TetherAddressUpdate($id:ID!,$address:CustomerAddressInput,$default:Boolean){customerAddressUpdate(addressId:$id,address:$address,defaultAddress:$default){customerAddress{${U}}${b}}}`,addressDelete:`mutation TetherAddressDelete($id:ID!){customerAddressDelete(addressId:$id){deletedAddressId ${b}}}`,returnRequest:`mutation TetherReturn($orderId:ID!,$lines:[RequestedLineItemInput!]!){orderRequestReturn(orderId:$orderId,requestedLineItems:$lines){return{id status}${b}}}`},ie={pause:{document:`mutation TetherPause($id:ID!){subscriptionContractPause(subscriptionContractId:$id){contract{id status}${b}}}`,payload:"subscriptionContractPause",input:"contract"},resume:{document:`mutation TetherResume($id:ID!){subscriptionContractActivate(subscriptionContractId:$id){contract{id status}${b}}}`,payload:"subscriptionContractActivate",input:"contract"},cancel:{document:`mutation TetherCancel($id:ID!){subscriptionContractCancel(subscriptionContractId:$id){contract{id status}${b}}}`,payload:"subscriptionContractCancel",input:"contract"},skip:{document:`mutation TetherSkip($input:SubscriptionBillingCycleInput!){subscriptionBillingCycleSkip(billingCycleInput:$input){billingCycle{cycleIndex skipped}${b}}}`,payload:"subscriptionBillingCycleSkip",input:"billingCycle"},unskip:{document:`mutation TetherUnskip($input:SubscriptionBillingCycleInput!){subscriptionBillingCycleUnskip(billingCycleInput:$input){billingCycle{cycleIndex skipped}${b}}}`,payload:"subscriptionBillingCycleUnskip",input:"billingCycle"}};function ge(e){return typeof e=="string"&&Object.hasOwn(ie,e)}function u(e,t){if(!(e===null||typeof e!="object"))return e[t]}function V(e){let t=u(e,"nodes");return Array.isArray(t)?t:[]}var we=/^\d{1,20}$/;function S(e,t){let s=typeof t=="string"||typeof t=="number"?String(t):"",r=we.test(s)?`gid://shopify/${e}/${s}`:s;if(!new RegExp(`^gid://shopify/${e}/\\d+(\\?[\\w=&.-]{1,80})?$`).test(r))throw new p(`${e} id`);return r}var Ae=/[\u0000-\u001f\u007f]/g;function B(e,t=255){if(e==null||e==="")return;if(typeof e!="string"&&typeof e!="number")throw new p("text");let s=String(e).replace(Ae,"").trim();if(s.length>t)throw new p("too long");return s}var Ie=["firstName","lastName","company","address1","address2","city","zip","zoneCode","phoneNumber"];function Ee(e,{clear:t=!1}={}){if(e===null||typeof e!="object"||Array.isArray(e))throw new p("address");let s=e,r={};for(let o of Ie){let i=B(s[o]);i!==void 0?r[o]=i:t&&o in s&&(s[o]===""||s[o]===null)&&(r[o]="")}let n=B(s.countryCode);if(n!==void 0){let o=n.toUpperCase();if(!/^[A-Z]{2}$/.test(o))throw new p("countryCode");r.countryCode=o}return r}function $(e,t){let s=u(e,t),r=u(s,"userErrors");if(Array.isArray(r)&&r.length){let n=r;throw new p(n.map(o=>o.message).join(" | "),{userErrors:n})}return s}var ae={async order(e,{id:t}){let s=await e(_.order,{id:S("Order",t)});return{order:u(s,"order")??null}},async profile(e,{firstName:t,lastName:s}){let r={};if(t!==void 0&&(r.firstName=B(t,100)??""),s!==void 0&&(r.lastName=B(s,100)??""),!Object.keys(r).length)throw new p("nothing to change");let n=$(await e(L.profile,{input:r}),"customerUpdate");return{customer:u(n,"customer")}},async addresses(e){let t=u(await e(_.addresses,{}),"customer"),s=u(u(t,"defaultAddress"),"id")??null;return{addresses:V(u(t,"addresses")).map(n=>({...n,isDefault:u(n,"id")===s}))}},async saveAddress(e,{id:t,address:s,isDefault:r}){let n=Ee(s??{},{clear:!!t}),o=r===!0;if(!t){let d=await e(L.addressCreate,{address:n,default:o});return{address:u($(d,"customerAddressCreate"),"customerAddress")}}let i=await e(L.addressUpdate,{id:S("CustomerAddress",t),address:Object.keys(n).length?n:null,default:o?!0:null});return{address:u($(i,"customerAddressUpdate"),"customerAddress")}},async deleteAddress(e,{id:t}){let s=await e(L.addressDelete,{id:S("CustomerAddress",t)});return{deleted:u($(s,"customerAddressDelete"),"deletedAddressId")}},async subscriptions(e){let t=u(u(await e(_.subscriptions,{}),"customer"),"subscriptionContracts");return{subscriptions:V(t)}},async subscription(e,{id:t,action:s}){if(!ge(s))throw new p("action");let r=S("SubscriptionContract",t),n=u(u(await e(_.contract,{id:r}),"customer"),"subscriptionContract");if(!n)throw new p("not found",{status:404});let{document:o,payload:i,input:d}=ie[s];if(d==="contract")return{contract:u($(await e(o,{id:r}),i),"contract")};let c=V(u(n,"upcomingBillingCycles")),P=s==="unskip",k=c.find(be=>!!u(be,"skipped")===P);if(!k)throw new p(P?"nothing skipped":"no upcoming delivery to skip");let he={input:{contractId:r,selector:{index:u(k,"cycleIndex")}}};return{billingCycle:u($(await e(o,he),i),"billingCycle")}},async requestReturn(e,{orderId:t,lines:s}){let r=S("Order",t);if(!Array.isArray(s)||!s.length||s.length>50)throw new p("lines");let n=s.map(i=>{let d=Number(u(i,"quantity"));if(!Number.isInteger(d)||d<1||d>1e3)throw new p("quantity");let c={lineItemId:S("LineItem",u(i,"lineItemId")),quantity:d},P=u(i,"reasonId");P&&(c.returnReasonDefinitionId=S("ReturnReasonDefinition",P));let k=B(u(i,"note"),300);return k&&(c.customerNote=k),c}),o=await e(L.returnRequest,{orderId:r,lines:n});return{return:u($(o,"orderRequestReturn"),"return")}}};var X=null;function Te(e){X=e}function v(){if(!X)throw new Error("account-pages: loaded without the account part");return X}var xe={order:e=>["GET",`/order?id=${encodeURIComponent(String(e.id))}`],addresses:()=>["GET","/addresses"],subscriptions:()=>["GET","/subscriptions"],profile:()=>["POST","/profile"],saveAddress:()=>["POST","/address"],deleteAddress:()=>["POST","/address/delete"],subscription:()=>["POST","/subscription"],requestReturn:()=>["POST","/return"]};async function T(e,t={}){let{serverBase:s,serverFetch:r,query:n}=v();if(s()){let[i,d]=xe[e](t),c=await r(d,i==="POST"?{method:i,body:t}:{});if(c.kind==="logged-out")throw new D("Not logged in",{code:"NOT_LOGGED_IN",status:401});return c.data}let o=ae[e];if(!o)throw new D(`Customer accounts: no operation ${e}`);try{return await o(async(i,d)=>await n(i,d),t)}catch(i){throw i instanceof p?new D(i.message,{code:"INVALID",userErrors:i.userErrors}):i}}function M(e){v().reset(),se("account-updated",{what:e})}async function K(e){return(await T("order",{id:e})).order??null}async function Q({firstName:e,lastName:t}={}){let s=await T("profile",{firstName:e,lastName:t});return M("profile"),s.customer}async function W(){return(await T("addresses")).addresses}async function H(e,{id:t,isDefault:s}={}){let r=await T("saveAddress",{id:t,address:e,isDefault:s});return M("addresses"),r.address}async function Y(e){let t=await T("deleteAddress",{id:e});return M("addresses"),t.deleted}async function Z(){return(await T("subscriptions")).subscriptions}async function J(e,t){let s=await T("subscription",{id:e,action:t});return M("subscriptions"),s}async function ee(e,t){let s=await T("requestReturn",{orderId:e,lines:t});return M("orders"),s.return}function Ce(e){let t=m(e,"order")||"";return t.startsWith("url:")?new URL(window.location.href).searchParams.get(t.slice(4)||"id"):t}function F(e,t){e.setAttribute(y("order-state"),t),f(e,"order-show",{loading:t==="loading",ready:!1,"not-found":t==="not-found",error:t==="error"})}var Se=e=>({name:e.name,date:C(e.processedAt),total:g(e.totalPrice),subtotal:g(e.subtotal),shipping:g(e.totalShipping),tax:g(e.totalTax),status:E(e.financialStatus),fulfillment:E(e.fulfillmentStatus),"shipping-address":N(e.shippingAddress),"billing-address":N(e.billingAddress),items:e.lineItems.nodes.map(t=>`${t.quantity}\xD7 ${t.title||t.name}`).join(", "),cancelled:e.cancelledAt?C(e.cancelledAt):""});function $e(e,t){let s=t.trackingInformation[0],r={status:E(t.latestShipmentStatus||t.status),date:C(t.createdAt),eta:t.estimatedDeliveryAt?C(t.estimatedDeliveryAt):"",company:s?.company||"",number:s?.number||""};O(e,"fulfillment-bind").forEach(n=>{let o=m(n,"fulfillment-bind")??"";if(o==="url"){let d=s?.url,c=z(d);c&&d&&n.setAttribute("href",d),ne(n,c);return}let i=w(r,o);i!==void 0&&h(n,i)})}function De(e,t){let s=new Map((t.returnInformation?.returnableLineItems.nodes??[]).map(i=>[i.lineItem?.id??"",i.quantity])),r=t.fulfillments.nodes,n=Se(t);l(e,a("order-bind")).forEach(i=>{let d=m(i,"order-bind")??"";if(d==="url"){t.statusPageUrl&&z(t.statusPageUrl)&&i.setAttribute("href",t.statusPageUrl);return}let c=w(n,d);c!==void 0&&h(i,c)}),l(e,a("order-lines")).forEach(i=>{oe(i,t.lineItems.nodes,s)}),l(e,a("order-fulfillments")).forEach(i=>{x(i,r,$e)});let o=e.dataset.tetherReturned==="1";f(e,"order-show",{loading:!1,ready:!0,error:!1,"not-found":!1,cancelled:!!t.cancelledAt,shipped:r.length>0,returnable:[...s.values()].some(i=>i>0),"has-returns":!o&&t.returns.nodes.length>0,"return-requested":o}),e.setAttribute(y("order-state"),"ready")}async function j(e){let t=Ce(e);if(!t){e.setAttribute(y("order-state"),"not-found"),f(e,"order-show",{"not-found":!0});return}F(e,"loading");let s;try{s=await K(t)}catch(r){I(r,"order"),F(e,"error");return}if(!s){F(e,"not-found");return}e.dataset.tetherOrderId=s.id,De(e,s)}var de=[],ue=e=>de.find(t=>t.id===e),Oe=e=>({formatted:N(e),name:[e.firstName,e.lastName].filter(Boolean).join(" "),company:e.company||"",address1:e.address1||"",address2:e.address2||"",city:e.city||"",zip:e.zip||"",province:e.province||"",country:e.country||"",phone:e.phoneNumber||""});function ve(e,t){e.dataset.tetherAddress=t.id;let s=Oe(t);O(e,"address-bind").forEach(r=>{let n=w(s,m(r,"address-bind")??"");n!==void 0&&h(r,n)}),f(e,"address-show",{default:t.isDefault,"not-default":!t.isDefault})}async function R(e){let t=l(e,a("addresses"));if(!t.length)return;f(e,"show",{"addresses-loading":!0,"addresses-empty":!1});let s;try{s=await W()}catch(r){I(r,"addresses"),f(e,"show",{"addresses-loading":!1,"addresses-error":!0});return}de=s,f(e,"show",{"addresses-loading":!1,"addresses-empty":s.length===0,"addresses-error":!1}),t.forEach(r=>{x(r,s,ve)})}function Re(e){let t=e.upcomingBillingCycles.nodes[0],s=e.status,r=s==="ACTIVE";return{active:r,paused:s==="PAUSED",cancelled:s==="CANCELLED",expired:s==="EXPIRED",failed:s==="FAILED",skipped:!!t?.skipped,"can-pause":r,"can-resume":s==="PAUSED"||s==="FAILED","can-skip":r&&t!==void 0&&!t.skipped,"can-unskip":r&&!!t?.skipped,"can-cancel":r||s==="PAUSED"||s==="FAILED"}}function qe(e){let t=E(e.deliveryPolicy?.interval);if(!t)return"";let s=e.deliveryPolicy?.intervalCount?.count??1;return s<=1?t:`${s} ${t}s`}function Pe(e){let t=e.lines.nodes,s=t.reduce((r,n)=>r+Number.parseFloat(n.lineDiscountedPrice?.amount??"0"),0);return{status:E(e.status),next:e.nextBillingDate?C(e.nextBillingDate):"",interval:qe(e),products:t.map(r=>`${r.quantity}\xD7 ${r.title}`).join(", "),total:t.length?g({amount:String(s),currencyCode:e.currencyCode}):"",delivery:g(e.deliveryPrice)}}function ke(e,t){let s={title:t.title,"variant-title":t.variantTitle||"",quantity:String(t.quantity),price:g(t.currentPrice)};O(e,"subscription-line").forEach(r=>{let n=m(r,"subscription-line")??"";if(n==="image"){t.image&&re(r,t.image.url,t.image.altText||t.title);return}let o=w(s,n);o!==void 0&&h(r,o)})}function Ne(e,t){e.dataset.tetherSubscription=t.id,e.setAttribute(y("subscription-status"),E(t.status));let s=Pe(t);l(e,a("subscription-bind")).forEach(n=>{let o=w(s,m(n,"subscription-bind")??"");o!==void 0&&h(n,o)}),l(e,a("subscription-lines")).forEach(n=>{x(n,t.lines.nodes,ke)});let r=Re(t);f(e,"subscription-show",r),l(e,a("subscription-action")).forEach(n=>{let o=w(r,`can-${m(n,"subscription-action")??""}`)===!0;"disabled"in n&&(n.disabled=!o),n.classList.toggle("is-disabled",!o)})}async function G(e){let t=l(e,a("subscriptions"));if(!t.length)return;f(e,"show",{"subscriptions-loading":!0,"subscriptions-empty":!1});let s;try{s=await Z()}catch(r){I(r,"subscriptions"),f(e,"show",{"subscriptions-loading":!1,"subscriptions-error":!0});return}f(e,"show",{"subscriptions-loading":!1,"subscriptions-empty":s.length===0,"subscriptions-error":!1}),t.forEach(r=>{x(r,s,Ne)})}var Le=["pause","resume","skip","unskip","cancel"],Ue=e=>Le.some(t=>t===e),le=e=>e.tagName==="INPUT",fe=e=>e.tagName==="FORM";function ce(e){let t={};for(let s of e.elements){if(!le(s)&&s.tagName!=="SELECT"&&s.tagName!=="TEXTAREA")continue;let r=s;!r.name||r.type==="submit"||r.type==="button"||r.type==="checkbox"||(t[r.name]=r.value)}return t}function Be(e){let s=(e instanceof D?e.userErrors??[]:[]).map(n=>n.message).join(" ");return s||(e instanceof Error&&e.message?e.message:"Something went wrong").replace(/^Customer accounts: /,"")}async function q(e,t){if(e.getAttribute("aria-busy")==="true")return"busy";e.classList.add("is-busy"),e.setAttribute("aria-busy","true"),e.setAttribute(y("form-state"),"pending");let s=l(e,a("form-error"));s.forEach(r=>{h(r,"")});try{return await t(),e.setAttribute(y("form-state"),"saved"),"saved"}catch(r){return e.setAttribute(y("form-state"),"error"),s.forEach(n=>{h(n,Be(r))}),"error"}finally{e.classList.remove("is-busy"),e.removeAttribute("aria-busy")}}var te=e=>{let t=m(e,"confirm");return!t||window.confirm(t)},pe=e=>{e.reset(),delete e.dataset.tetherAddressId};function Me(e){let t=e.target;if(t instanceof window.HTMLFormElement){if(t.matches(a("profile-form")))e.preventDefault(),q(t,async()=>{await Q(ce(t)),await v().bindCustomer(document)});else if(t.matches(a("address-form"))){e.preventDefault();let s=t.dataset.tetherAddressId,r=t.elements.namedItem("isDefault"),n=r instanceof window.HTMLInputElement&&r.checked;q(t,async()=>{await H(ce(t),{id:s,isDefault:n}),pe(t),await R(document)})}}}function Fe(e){let t=ue(e),s=document.querySelector(a("address-form"));if(!(!t||!s||!fe(s))){s.dataset.tetherAddressId=t.id;for(let r of s.elements){if(!le(r)&&r.tagName!=="SELECT"&&r.tagName!=="TEXTAREA")continue;let n=r;if(n.name==="isDefault")n.checked=t.isDefault;else if(n.name&&Object.hasOwn(t,n.name)){let o=Object.entries(t).find(([i])=>i===n.name)?.[1];n.value=typeof o=="string"?o:""}}s.scrollIntoView({block:"nearest"})}}function _e(e,t){let s=t.closest("[data-tether-address]"),r=s instanceof window.HTMLElement?s.dataset.tetherAddress:void 0;if(t.closest(a("address-new"))){let o=document.querySelector(a("address-form"));return o&&fe(o)&&pe(o),!0}if(!(s instanceof window.HTMLElement)||!r)return!1;let n=t.closest(a("address-delete"));if(n){if(e.preventDefault(),!te(n))return!0;q(s,async()=>{await Y(r),await R(document)})}else if(t.closest(a("address-default")))e.preventDefault(),q(s,async()=>{await H(null,{id:r,isDefault:!0}),await R(document)});else if(t.closest(a("address-edit")))e.preventDefault(),Fe(r);else return!1;return!0}function He(e){let t=e.closest("[data-tether-subscription]"),s=m(e,"subscription-action"),r="disabled"in e&&e.disabled===!0||e.classList.contains("is-disabled");if(!(t instanceof window.HTMLElement)||!Ue(s)||r||!te(e))return;let n=t.dataset.tetherSubscription??"";t.setAttribute(y("subscription-state"),"pending"),q(t,async()=>{await J(n,s),await G(document)}).then(o=>{let i=o==="error"?"error":"done";l(document,"[data-tether-subscription]").filter(d=>d===t||d.dataset.tetherSubscription===n).forEach(d=>{d.setAttribute(y("subscription-state"),i)})})}function je(e){let t=e.querySelector(a("return-quantity")),s=e.querySelector(a("return-reason")),r=e.querySelector(a("return-note")),n=c=>c&&"value"in c&&typeof c.value=="string"?c.value:"",o={lineItemId:e.dataset.tetherLine??"",quantity:Number.parseInt(n(t)||"0",10)},i=n(s);i&&(o.reasonId=i);let d=n(r);return d&&(o.note=d),o}function Ge(e){let t=e.closest(a("order"));if(!(t instanceof window.HTMLElement))return;let s=t.dataset.tetherOrderId;if(!s)return;let r=l(t,"[data-tether-line]").map(je).filter(n=>n.quantity>0);!r.length||!te(e)||q(t,async()=>{await ee(s,r),t.dataset.tetherReturned="1",await j(t)})}function ze(e){let t=e.target;if(!(t instanceof window.Element)||_e(e,t))return;let s=t.closest(a("subscription-action"));if(s instanceof window.HTMLElement){e.preventDefault(),He(s);return}let r=t.closest(a("order-return"));r instanceof window.HTMLElement&&(e.preventDefault(),Ge(r))}function me(){document.addEventListener("submit",Me,!0),document.addEventListener("input",e=>{let t=e.target;t instanceof window.HTMLElement&&t.closest(a("profile-form"))&&(t.dataset.tetherTouched="1")}),document.addEventListener("click",ze)}var Ve=["firstName","lastName"];async function Xe(e){let t=await v().customer().catch(r=>(I(r,"account"),null));if(!t||e.tagName!=="FORM")return;let s=e.elements;Ve.forEach(r=>{let n=s.namedItem(r);(n instanceof window.HTMLInputElement||n instanceof window.HTMLTextAreaElement)&&!n.dataset.tetherTouched&&(n.value=t[r]??"")})}var ye=!1;async function qt(e=document){ye||(ye=!0,me()),await Promise.all([...l(e,a("order")).map(t=>j(t)),...l(e,a("profile-form")).map(t=>Xe(t)),R(e),G(e)])}function Pt(e){l(e,a("order")).forEach(t=>{F(t,"error")}),f(e,"show",{"addresses-loading":!1,"addresses-error":!0,"subscriptions-loading":!1,"subscriptions-error":!0})}export{W as addresses,Y as deleteAddress,K as order,qt as renderPages,ee as requestReturn,H as saveAddress,Te as setup,Pt as showUnavailable,J as subscriptionAction,Z as subscriptions,Q as updateProfile};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as g}from"./chunk-MPTQYLSM.js";import"./chunk-NEHZ37TU.js";import"./chunk-JOOEXYF3.js";import{b as c,g as w,i as l,p as k,s as f}from"./chunk-YJLWX2HK.js";import"./chunk-FOMSMIAM.js";var F=500,T={"default-banner":"https://cdn.shopify.com/shopifycloud/privacy-banner/storefront-banner.js",other:"https://cdn.shopify.com/shopifycloud/consent-tracking-api/v0.2/consent-tracking-api.js"},N="https://cdn.shopify.com/storefront/analytics/shopify.js";function y(){return Reflect.get(window,"Shopify")}function S(){let t=c.analytics?.shopify;return typeof t=="object"?t:{}}function x(){try{let t=y()?.customerPrivacy;return t?.consentStatus==="loaded"&&t.currentVisitorConsent?.()?.analytics!=="no"&&!!t.analyticsProcessingAllowed?.()}catch{return!1}}function V(t){let e=`hydrogen:${t}`;return{getTrackingValues:()=>{let n=x()?y()?.customerPrivacy?.__internal:void 0;return{uniqueToken:n?.uniqueToken?.({generateFallback:!0,tag:e})??"",visitToken:n?.visitToken?.({generateFallback:!0,tag:e})??""}}}}var D=t=>(e,n)=>{let o=t.subscribers.get(e)??new Set;return t.subscribers.set(e,o),o.add(n),()=>t.subscribers.get(e)?.delete(n)};function M(t){let e=[],n=[],o={shop:{shopId:t,channel:"headless"},consent:{mode:S().consent||"no-banner"}},r=()=>{if(x())for(let i of e)for(;i.next<n.length;i.next+=1){let s=n[i.next];s&&O(i,s)}},a={getConfig:()=>o,publish(i,s={}){if(n.push({event:i,payload:{shop:o.shop,url:window.location.href,...s}}),n.length>F){n.shift();for(let d of e)d.next=Math.max(0,d.next-1)}r()},addDestination(i){let s={name:i.name,next:0,subscribers:new Map,context:V(i.name)};return Promise.resolve(i.setup?.({subscribe:D(s),getConfig:a.getConfig})).then(()=>{e.push(s),r()}).catch(d=>{l(d,`analytics ${i.name}`)}),()=>{let d=e.indexOf(s);d>=0&&e.splice(d,1)}}};return document.addEventListener("consentTrackingApiLoaded",r),document.addEventListener("visitorConsentCollected",r),a}function O(t,{event:e,payload:n}){t.subscribers.get(e)?.forEach(o=>{try{o(n,t.context)}catch(r){l(r,`analytics ${t.name}`)}})}function C(t){let e=document.createElement("script");e.src=t,e.async=!0,document.head.appendChild(e)}async function E(t){let e=y();if(e?.analytics)return e.analytics;let n=await k("query TetherShopId { shop { id } }",{},{context:!1}),o=y()??{};Reflect.set(window,"Shopify",o),o.country=(c.country||"US").toUpperCase(),o.locale=(c.locale||"en").split("-")[0]?.toLowerCase()??"en",o.shop=c.domain,o.routes={root:"/",match:()=>({pageTemplateName:t()?"product":"page"}),...o.routes};let{proxy:r,consent:a}=S();o.customerPrivacy={...o.customerPrivacy,config:r?{isHeadless:!0,asyncConsent:!0,asyncVisitorState:!0,consentDomain:window.location.host}:{isHeadless:!0,storefrontAccessToken:c.token,checkoutRootDomain:c.domain}};let i=M(n.shop.id);return o.analytics=i,C(a==="default-banner"?T["default-banner"]:T.other),C(N),w("shopify analytics ready",n.shop.id),i}var U="Default Title",A=100,$=200,j=t=>t?parseFloat(t.amount):0,L=t=>(t??"").split("/").pop()??"";function v(t,e,n=1,o){let r={item_id:e?.sku||L(e?.id)||L(t.id),item_name:t.title,item_variant:e?.title===U?void 0:e?.title,item_brand:t.vendor||void 0,item_category:t.productType||void 0,price:j(e?.price??t.priceRange?.minVariantPrice),quantity:n};return o!=null&&(r.index=o),r}var m=(t,e=t.quantity)=>v(t.merchandise.product,t.merchandise,e);function p(t){let e=t.reduce((n,o)=>n+o.price*(o.quantity||1),0);return Math.round(e*A)/A}var B=(t,e,n=1)=>({id:t.id,title:t.title,price:e.price,vendor:t.vendor||"",variantId:e.id,variantTitle:e.title,quantity:n,sku:e.sku||void 0,productType:t.productType||void 0}),P=t=>({id:t.id,quantity:t.quantity,merchandise:{id:t.merchandise.id,title:t.merchandise.title,price:t.merchandise.price,sku:t.merchandise.sku,product:{id:t.merchandise.product.id,title:t.merchandise.product.title,vendor:t.merchandise.product.vendor||"",handle:t.merchandise.product.handle}}});function H(){let{dataLayer:t}=c.analytics??{},e=typeof t=="string"?t:"dataLayer",n=Reflect.get(window,e);if(Array.isArray(n))return n;let o=[];return Reflect.set(window,e,o),o}function W(t,e){let n=H();if(!e){n.push({event:t});return}n.push({ecommerce:null}),n.push({event:t,ecommerce:e})}var h=null,b=null,I=null,_=new WeakSet;function u(t,e,n,o){c.analytics?.dataLayer&&W(t,e),h&&n&&h.publish(n,o);try{c.analytics?.onEvent?.(t,e)}catch(r){l(r,"analytics onEvent")}}function q(){let t=document.querySelector(f("product"));return!t||t.closest(`${f("collection")},${f("products")},${f("recommendations")}`)?null:Reflect.get(window,"Tether").getScope(t)}function G({product:t,scope:e}){if(!e||_.has(e.root)||q()!==e)return;_.add(e.root);let n=e.variant??t.variants.nodes[0],o=[v(t,n)],r=n?[B(t,n)]:[];u("view_item",{currency:n?.price.currencyCode,value:p(o),items:o},"product_viewed",{products:r})}function X({products:t=[],collection:e}){let n=t.slice(0,$).map((r,a)=>v(r,r.variants.nodes[0],1,a)),o={item_list_id:e?.handle||"products",item_list_name:e?.title||"Products"};if(!e){u("view_item_list",{...o,items:n});return}u("view_item_list",{...o,items:n},"collection_viewed",{collection:{id:e.id,handle:e.handle}})}function Y(t,e,n){let o=t?.cost.totalAmount.currencyCode;e.forEach((r,a)=>{let i=n.get(a),s=r.quantity-(i?.quantity??0);if(s<=0)return;let d=[m(r,s)];u("add_to_cart",{currency:o,value:p(d),items:d},"product_added_to_cart",{cart:t,prevCart:I,currentLine:P(r),prevLine:i?P(i):void 0})}),n.forEach((r,a)=>{let i=r.quantity-(e.get(a)?.quantity??0);if(i<=0)return;let s=[m(r,i)];u("remove_from_cart",{currency:o,value:p(s),items:s})})}function Z({cart:t,reason:e}){let n=new Map((t?.lines.nodes??[]).map(o=>[o.merchandise.id,o]));b&&e!=="load"&&e!=="pending"&&Y(t,n,b),b=n,I=t}function z(){let t=g.lines.map(e=>m(e));u("view_cart",{currency:g.state?.cost.totalAmount.currencyCode,value:p(t),items:t})}function J({cart:t}){let e=t.lines.nodes.map(o=>m(o)),n=t.discountCodes.filter(o=>o.applicable).map(o=>o.code);u("begin_checkout",{currency:t.cost.totalAmount.currencyCode,value:p(e),coupon:n.join(",")||void 0,items:e})}function R(t){let e=t.type.slice(c.prefix.length+1),n=Reflect.get(t,"detail");switch(e){case"product-ready":G(n);break;case"list-ready":X(n);break;case"cart-updated":Z(n);break;case"cart-toggled":n.open&&z();break;case"checkout":J(n);break;case"search":{let{term:o}=n;o&&u("search",{search_term:o},"search_viewed",{searchTerm:o});break}case"page":_=new WeakSet,h?.publish("page_viewed",{});break;default:break}}async function it(t){c.analytics?.shopify&&(h=await E(()=>q()!=null).catch(e=>(l(e,"shopify analytics"),null))),h?.publish("page_viewed",{}),t.splice(0).forEach(R),t.push=(...e)=>(e.forEach(R),t.length)}export{it as startAnalytics};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{b as u,j as s,k as f,p as y}from"./chunk-YJLWX2HK.js";var p=["saved","server","webflow"],b="tether_country",w=()=>`${u.prefix}:market:${u.domain}`;function h(){if(typeof window>"u")return null;try{return window.localStorage}catch{return null}}function C(){let e=typeof window<"u";return{document:typeof document<"u"?document:null,location:e?window.location:null,storage:h(),global:e?window:{}}}function L(e){try{return JSON.parse(e)}catch(t){if(t instanceof SyntaxError)return null;throw t}}function S(e){if(typeof e!="object"||e===null)return null;let t=s(Reflect.get(e,"country"));if(!t)return null;let n=Reflect.get(e,"locale");return{country:t,language:f(Reflect.get(e,"language"))??void 0,locale:typeof n=="string"&&n?n:void 0}}function E(e=h()){let t=e?.getItem(w());return t?S(L(t)):null}function R(e,t){if(!e||!t)return null;let n=e.cookie.split(";").map(r=>r.trim()).find(r=>r.startsWith(`${t}=`));return n?decodeURIComponent(n.slice(t.length+1)):null}function g(e,t){if(!e||!t)return null;let n=t.toLowerCase(),r=Object.keys(e).find(o=>o.toLowerCase()===n);return r?e[r]??null:null}function A(e,t){let n=r=>t.document?.querySelector(`meta[name="${r}"]`)?.getAttribute("content");return{country:Reflect.get(t.global,"TETHER_COUNTRY")||n("tether-country")||R(t.document,e.cookie||b),language:Reflect.get(t.global,"TETHER_LANGUAGE")||n("tether-language")}}function O(e,t){let n=(t.location?.pathname??"/").split("/").find(i=>i!==""),r=g(e.locales,n);if(r)return r;let o=t.document?.documentElement.getAttribute("lang");if(!o)return null;let c=g(e.locales,o)??g(e.locales,o.split("-")[0]);if(c)return{locale:o,...c};if(e.locales&&e.strict)return null;let l=o.split("-")[1];return{language:o,country:l?.length===2?l:null,locale:o}}function x(e,t,n){switch(e){case"saved":return t.remember===!1?null:E(n.storage);case"server":return A(t,n);case"webflow":return O(t,n)}}var I=e=>p.some(t=>t===e);function T(e){return e?e===!0?p:[e].flat().filter(I):["saved"]}function M(e=u.markets,t=C()){let n=e??{};if(n.detect===!1)return{};let r={},o={sources:r};for(let c of T(n.detect)){let l=x(c,n,t);if(!l)continue;let i=s(l.country),d=f(l.language),m=typeof l.locale=="string"&&l.locale?l.locale:null;i&&!o.country&&(o.country=i,r.country=c),d&&!o.language&&(o.language=d,r.language=c),m&&!o.locale&&(o.locale=m,r.locale=c)}return o}function z(e,t){try{localStorage.setItem(e,t)}catch{return}}function $(e){if(u.markets?.remember===!1)return;let t={country:e.country,language:e.language||void 0,locale:e.locale||void 0};z(w(),JSON.stringify(t))}var a=null;function q(){a=null}var N="query TetherLocalization{localization{country{isoCode name currency{isoCode symbol}}language{isoCode name}availableCountries{isoCode name currency{isoCode symbol}availableLanguages{isoCode name}}}}";function H(){return a||(a=y(N).then(t=>t.localization,t=>{throw a=null,t})),a}export{E as a,M as b,$ as c,q as d,H as e};
|