@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.
Files changed (259) hide show
  1. package/README.md +11 -11
  2. package/dist/esm/astro.js +2 -2
  3. package/dist/esm/chunks/account-UIPWJQ3Y.js +1 -0
  4. package/dist/esm/chunks/account-pages-6ROA6Y4V.js +1 -0
  5. package/dist/esm/chunks/analytics-2V632IRV.js +1 -0
  6. package/dist/esm/chunks/chunk-4GCXM764.js +1 -0
  7. package/dist/esm/chunks/chunk-56FCLJJD.js +1 -0
  8. package/dist/esm/chunks/chunk-5Z5O3767.js +1 -0
  9. package/dist/esm/chunks/chunk-6OZJCAGZ.js +1 -0
  10. package/dist/esm/chunks/chunk-A5SYCVQM.js +1 -0
  11. package/dist/esm/chunks/chunk-BA4LQJJQ.js +1 -0
  12. package/dist/esm/chunks/chunk-BFTZR4OB.js +4 -0
  13. package/dist/esm/chunks/chunk-C5JYQCWH.js +1 -0
  14. package/dist/esm/chunks/chunk-E5YO6274.js +1 -0
  15. package/dist/esm/chunks/chunk-MPDWM2NN.js +1 -0
  16. package/dist/esm/chunks/chunk-T44MUL4Y.js +1 -0
  17. package/dist/esm/chunks/chunk-YLUULATY.js +1 -0
  18. package/dist/esm/chunks/diagnose-3FEKRIEK.js +14 -0
  19. package/dist/esm/chunks/extras-6POFBNOP.js +1 -0
  20. package/dist/esm/chunks/markets-ui-FD6NI2OG.js +1 -0
  21. package/dist/esm/chunks/search-UJ73YBQ5.js +1 -0
  22. package/dist/esm/index.js +1 -1
  23. package/dist/esm/server.js +2 -2
  24. package/dist/presets/barba.esm.js +19 -23
  25. package/dist/tether.account-pages.js +320 -365
  26. package/dist/tether.account-pages.min.js +1 -1
  27. package/dist/tether.account.js +242 -491
  28. package/dist/tether.account.min.js +1 -1
  29. package/dist/tether.analytics.js +66 -88
  30. package/dist/tether.analytics.min.js +1 -1
  31. package/dist/tether.diagnose.js +36 -33
  32. package/dist/tether.diagnose.min.js +2 -2
  33. package/dist/tether.extras.js +28 -36
  34. package/dist/tether.extras.min.js +1 -1
  35. package/dist/tether.js +1270 -894
  36. package/dist/tether.markets.js +73 -90
  37. package/dist/tether.markets.min.js +1 -1
  38. package/dist/tether.min.js +0 -0
  39. package/dist/tether.min.js.map +4 -4
  40. package/dist/tether.search.js +44 -68
  41. package/dist/tether.search.min.js +1 -1
  42. package/dist/types/server/src/shared/account-orders.d.ts +44 -0
  43. package/dist/types/server/src/shared/account-routes.d.ts +153 -0
  44. package/dist/types/server/src/shared/api-versions.d.ts +13 -0
  45. package/dist/types/server/src/shared/error-message.d.ts +2 -0
  46. package/dist/types/server/src/shared/json.d.ts +25 -0
  47. package/dist/types/server/src/shared/market-codes.d.ts +16 -0
  48. package/dist/types/src/account/addresses-view.d.ts +5 -0
  49. package/dist/types/src/account/answers.d.ts +40 -0
  50. package/dist/types/src/account/markup.d.ts +19 -0
  51. package/dist/types/src/account/operations.d.ts +41 -0
  52. package/dist/types/src/account/order-lines.d.ts +13 -0
  53. package/dist/types/src/account/order-list.d.ts +12 -0
  54. package/dist/types/src/account/order-page.d.ts +6 -0
  55. package/dist/types/src/account/page-answers.d.ts +182 -0
  56. package/dist/types/src/account/page-events.d.ts +2 -0
  57. package/dist/types/src/account/server-client.d.ts +60 -0
  58. package/dist/types/src/account/subscriptions-view.d.ts +2 -0
  59. package/dist/types/src/account-api.d.ts +18 -0
  60. package/dist/types/src/account-marks.d.ts +15 -0
  61. package/dist/types/src/account-pages.d.ts +8 -0
  62. package/dist/types/src/account.d.ts +64 -0
  63. package/dist/types/src/amounts.d.ts +12 -0
  64. package/dist/types/src/analytics-shopify.d.ts +41 -0
  65. package/dist/types/src/analytics.d.ts +2 -0
  66. package/dist/types/src/api-version.d.ts +28 -0
  67. package/dist/types/src/astro.d.ts +42 -0
  68. package/dist/types/src/cart/cart-shape.d.ts +43 -0
  69. package/dist/types/src/cart/checkout.d.ts +16 -0
  70. package/dist/types/src/cart/drawer.d.ts +18 -0
  71. package/dist/types/src/cart/mutations.d.ts +79 -0
  72. package/dist/types/src/cart/render.d.ts +17 -0
  73. package/dist/types/src/cart/store.d.ts +182 -0
  74. package/dist/types/src/cart.d.ts +106 -0
  75. package/dist/types/src/client.d.ts +100 -0
  76. package/dist/types/src/collection.d.ts +25 -0
  77. package/dist/types/src/config-sections.d.ts +40 -0
  78. package/dist/types/src/config.d.ts +185 -0
  79. package/dist/types/src/controls/cart-controls.d.ts +2 -0
  80. package/dist/types/src/controls/discount.d.ts +2 -0
  81. package/dist/types/src/controls/extras.d.ts +5 -0
  82. package/dist/types/src/controls/navigation.d.ts +12 -0
  83. package/dist/types/src/diagnose-markup.d.ts +19 -0
  84. package/dist/types/src/diagnose.d.ts +60 -0
  85. package/dist/types/src/dom.d.ts +75 -0
  86. package/dist/types/src/extras.d.ts +21 -0
  87. package/dist/types/src/fragments.d.ts +158 -0
  88. package/dist/types/src/globals.d.ts +15 -0
  89. package/dist/types/src/images.d.ts +20 -0
  90. package/dist/types/src/index.d.ts +167 -0
  91. package/dist/types/src/initial.d.ts +56 -0
  92. package/dist/types/src/lazy.d.ts +28 -0
  93. package/dist/types/src/loader.d.ts +84 -0
  94. package/dist/types/src/market-codes.d.ts +6 -0
  95. package/dist/types/src/markets-ui.d.ts +32 -0
  96. package/dist/types/src/markets.d.ts +72 -0
  97. package/dist/types/src/markup.d.ts +131 -0
  98. package/dist/types/src/mounting.d.ts +46 -0
  99. package/dist/types/src/page-market.d.ts +27 -0
  100. package/dist/types/src/presets/barba.d.ts +56 -0
  101. package/dist/types/src/product-events.d.ts +2 -0
  102. package/dist/types/src/product-render.d.ts +17 -0
  103. package/dist/types/src/product-shape.d.ts +88 -0
  104. package/dist/types/src/product-state.d.ts +110 -0
  105. package/dist/types/src/product.d.ts +66 -0
  106. package/dist/types/src/repeaters.d.ts +16 -0
  107. package/dist/types/src/search.d.ts +33 -0
  108. package/dist/types/src/selling-plans.d.ts +64 -0
  109. package/dist/types/src/server.d.ts +88 -0
  110. package/dist/types/src/shopify-values.d.ts +46 -0
  111. package/dist/types/src/status.d.ts +33 -0
  112. package/dist/types/src/storage.d.ts +12 -0
  113. package/dist/types/src/store-html.d.ts +39 -0
  114. package/dist/types/src/storefront/catalog.d.ts +38 -0
  115. package/dist/types/src/storefront/json-ld.d.ts +20 -0
  116. package/dist/types/src/variant-render.d.ts +14 -0
  117. package/package.json +24 -23
  118. package/src/account/addresses-view.ts +58 -0
  119. package/src/account/answers.ts +79 -0
  120. package/src/account/markup.ts +36 -0
  121. package/src/account/operations.ts +145 -0
  122. package/src/account/{order-lines.js → order-lines.ts} +20 -41
  123. package/src/account/order-list.ts +107 -0
  124. package/src/account/order-page.ts +137 -0
  125. package/src/account/page-answers.ts +317 -0
  126. package/src/account/{page-events.js → page-events.ts} +55 -90
  127. package/src/account/server-client.ts +183 -0
  128. package/src/account/{subscriptions-view.js → subscriptions-view.ts} +25 -68
  129. package/src/account-api.ts +98 -0
  130. package/src/account-marks.ts +35 -0
  131. package/src/{account-pages.js → account-pages.ts} +29 -37
  132. package/src/account.ts +284 -0
  133. package/src/amounts.ts +64 -0
  134. package/src/{analytics-shopify.js → analytics-shopify.ts} +112 -107
  135. package/src/analytics.ts +315 -0
  136. package/src/{api-version.js → api-version.ts} +25 -31
  137. package/src/{astro.js → astro.ts} +110 -104
  138. package/src/cart/cart-shape.ts +25 -0
  139. package/src/cart/checkout.ts +101 -0
  140. package/src/cart/{drawer.js → drawer.ts} +18 -31
  141. package/src/cart/mutations.ts +237 -0
  142. package/src/cart/{render.js → render.ts} +68 -96
  143. package/src/cart/store.ts +279 -0
  144. package/src/{cart.js → cart.ts} +82 -128
  145. package/src/client.ts +391 -0
  146. package/src/{collection.js → collection.ts} +30 -38
  147. package/src/config-sections.ts +284 -0
  148. package/src/config.ts +480 -0
  149. package/src/controls/{cart-controls.js → cart-controls.ts} +22 -54
  150. package/src/controls/{discount.js → discount.ts} +13 -34
  151. package/src/controls/{extras.js → extras.ts} +15 -24
  152. package/src/controls/{navigation.js → navigation.ts} +24 -30
  153. package/src/{diagnose-markup.js → diagnose-markup.ts} +37 -72
  154. package/src/{diagnose.js → diagnose.ts} +95 -96
  155. package/src/dom.ts +268 -0
  156. package/src/{extras.js → extras.ts} +43 -52
  157. package/src/fragments.ts +261 -0
  158. package/src/globals.d.ts +15 -0
  159. package/src/{images.js → images.ts} +30 -43
  160. package/src/index.ts +415 -0
  161. package/src/initial.ts +101 -0
  162. package/src/lazy-iife.ts +197 -0
  163. package/src/lazy.ts +42 -0
  164. package/src/{loader.js → loader.ts} +149 -123
  165. package/src/market-codes.ts +6 -0
  166. package/src/{markets-ui.js → markets-ui.ts} +91 -68
  167. package/src/markets.ts +246 -0
  168. package/src/markup.ts +127 -0
  169. package/src/{mounting.js → mounting.ts} +88 -101
  170. package/src/{page-market.js → page-market.ts} +32 -38
  171. package/src/part-integrity.ts +7 -0
  172. package/src/parts/account-pages.ts +7 -0
  173. package/src/parts/account.ts +7 -0
  174. package/src/parts/analytics.ts +7 -0
  175. package/src/parts/diagnose.ts +7 -0
  176. package/src/parts/extras.ts +7 -0
  177. package/src/parts/markets.ts +7 -0
  178. package/src/parts/search.ts +7 -0
  179. package/src/presets/barba.ts +194 -0
  180. package/src/{product-events.js → product-events.ts} +33 -48
  181. package/src/{product-render.js → product-render.ts} +38 -77
  182. package/src/product-shape.ts +49 -0
  183. package/src/product-state.ts +318 -0
  184. package/src/{product.js → product.ts} +90 -126
  185. package/src/{repeaters.js → repeaters.ts} +40 -52
  186. package/src/{search.js → search.ts} +85 -103
  187. package/src/selling-plans.ts +145 -0
  188. package/src/server.ts +211 -0
  189. package/src/{shopify-values.js → shopify-values.ts} +24 -22
  190. package/src/{status.js → status.ts} +34 -54
  191. package/src/storage.ts +34 -0
  192. package/src/store-html.ts +359 -0
  193. package/src/storefront/catalog.ts +196 -0
  194. package/src/storefront/{json-ld.js → json-ld.ts} +45 -68
  195. package/src/{variant-render.js → variant-render.ts} +24 -50
  196. package/dist/esm/chunks/account-C3NP6HFB.js +0 -1
  197. package/dist/esm/chunks/account-pages-WLK374FO.js +0 -1
  198. package/dist/esm/chunks/analytics-G3SAWMNL.js +0 -1
  199. package/dist/esm/chunks/chunk-27PJJZGC.js +0 -1
  200. package/dist/esm/chunks/chunk-2FSXS55U.js +0 -1
  201. package/dist/esm/chunks/chunk-FCSWQDVV.js +0 -1
  202. package/dist/esm/chunks/chunk-FOMSMIAM.js +0 -1
  203. package/dist/esm/chunks/chunk-GZJTCLLW.js +0 -1
  204. package/dist/esm/chunks/chunk-HEQILZVR.js +0 -1
  205. package/dist/esm/chunks/chunk-JOOEXYF3.js +0 -4
  206. package/dist/esm/chunks/chunk-MPTQYLSM.js +0 -1
  207. package/dist/esm/chunks/chunk-NEHZ37TU.js +0 -3
  208. package/dist/esm/chunks/chunk-V74KGEJF.js +0 -1
  209. package/dist/esm/chunks/chunk-VT4CDUJJ.js +0 -1
  210. package/dist/esm/chunks/chunk-YJLWX2HK.js +0 -1
  211. package/dist/esm/chunks/diagnose-6VVGZNTY.js +0 -14
  212. package/dist/esm/chunks/extras-GJVRDFM3.js +0 -1
  213. package/dist/esm/chunks/markets-ui-UNB4PTA7.js +0 -1
  214. package/dist/esm/chunks/search-OU75RNQL.js +0 -1
  215. package/src/account/addresses-view.js +0 -92
  216. package/src/account/browser-session.js +0 -359
  217. package/src/account/markup.js +0 -48
  218. package/src/account/operations.js +0 -180
  219. package/src/account/order-list.js +0 -91
  220. package/src/account/order-page.js +0 -192
  221. package/src/account/server-client.js +0 -172
  222. package/src/account/shared.js +0 -111
  223. package/src/account/shopify-auth.js +0 -150
  224. package/src/account-api.js +0 -118
  225. package/src/account-links.js +0 -35
  226. package/src/account.js +0 -342
  227. package/src/amounts.js +0 -36
  228. package/src/analytics.js +0 -352
  229. package/src/cart/checkout.js +0 -97
  230. package/src/cart/mutations.js +0 -234
  231. package/src/cart/store.js +0 -326
  232. package/src/client.js +0 -267
  233. package/src/config.js +0 -349
  234. package/src/dom.js +0 -287
  235. package/src/fragments.js +0 -237
  236. package/src/index.js +0 -353
  237. package/src/initial.js +0 -87
  238. package/src/lazy-iife.js +0 -116
  239. package/src/lazy.js +0 -39
  240. package/src/market-codes.js +0 -35
  241. package/src/markets.js +0 -334
  242. package/src/markup.js +0 -64
  243. package/src/parts/account-pages.js +0 -6
  244. package/src/parts/account.js +0 -6
  245. package/src/parts/analytics.js +0 -6
  246. package/src/parts/diagnose.js +0 -6
  247. package/src/parts/extras.js +0 -6
  248. package/src/parts/markets.js +0 -6
  249. package/src/parts/search.js +0 -6
  250. package/src/presets/barba.js +0 -190
  251. package/src/product-state.js +0 -318
  252. package/src/selling-plans.js +0 -154
  253. package/src/server.js +0 -215
  254. package/src/store-html.js +0 -0
  255. package/src/storefront/catalog.js +0 -199
  256. package/types/astro.d.ts +0 -28
  257. package/types/barba.d.ts +0 -16
  258. package/types/index.d.ts +0 -577
  259. 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.js';
7
- import { productUrl } from '../shopify-values.js';
8
- /** @import { Product, ProductVariant } from '../fragments.js' */
9
- /** @import { Money } from '../shopify-values.js' */
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
- * @typedef {object} JsonLdOptions
13
- * @property {string} [url] The product page's absolute URL; default from the storefront's `siteUrl`.
14
- * @property {string} [brand] Default the product's vendor.
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
- /** @typedef {{ productPath: string, siteUrl: string | undefined }} JsonLdSite Where the product pages are. */
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
- /** @type {Readonly<Record<string, string>>} */
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
- * @param {Record<string, unknown>} value
43
- * @returns {Record<string, unknown>} `value` without its `undefined` fields, nested ones too.
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 /** @type {Record<string, unknown>} */ (copy);
46
+ return copy as Record<string, unknown>;
50
47
  }
51
48
 
52
- /** @param {string} gid */
53
- const numericId = (gid) => gid.split('/').pop() ?? gid;
49
+ const numericId = (gid: string) => gid.split('/').pop() ?? gid;
54
50
 
55
- /**
56
- * @param {ProductVariant} variant
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
- * @param {Product} product
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
- * @param {Product} product
94
- * @param {ProductVariant} variant
95
- * @param {string | undefined} pageUrl
96
- * @param {readonly string[]} images
97
- */
98
- function variantProduct(product, variant, pageUrl, images) {
99
- /** @type {[string, string][]} schema.org property name → the variant's value. */
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
- if (!site.siteUrl) return undefined;
123
- return new URL(productUrl(site.productPath, product.handle, product.id), site.siteUrl).toString();
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
- * @param {Product | null | undefined} product
128
- * @param {JsonLdOptions} options
129
- * @param {JsonLdSite} site
130
- * @returns {Record<string, unknown> | null} The schema.org object, `null` without a product.
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.js`'s view as
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.js';
6
- import { STATUS_KEYS, ownedBy } from './status.js';
7
- import { allocationFor } from './selling-plans.js';
8
- import { isValueAvailable, planBindTexts, variantBindTexts } from './product-state.js';
9
- import { isSelect, selectsIn } from './product-render.js';
10
- /** @import { Formatters, ProductView } from './product-state.js' */
11
- /** @import { RenderScope } from './product-render.js' */
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
- /** @type {Formatters} */
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 = (/** @type {string | null} */ authored) => view.selected[scope.optName(authored)] ?? '';
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, isSubscription } = view;
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, isSubscription && planId === purchase.planId);
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 = isSubscription ? (purchase.planId ?? '') : '';
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
- /** @type {Map<string, boolean>} */
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.isSubscription && subscribeText) text = subscribeText;
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.isSubscription);
163
- scope.root.setAttribute(attr('purchase-mode'), view.isSubscription ? 'subscription' : 'one-time');
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};