@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,17 @@
1
+ import type { CartWarning } from '../config.js';
2
+ import type { ShopifyCart, ShopifyCartLine, PendingQuantity } from './store.js';
3
+ /** What a render shows. */
4
+ interface CartView {
5
+ cart: ShopifyCart | null;
6
+ warnings: readonly CartWarning[];
7
+ /** Line id → the quantity quick clicks asked for. */
8
+ pending: ReadonlyMap<string, PendingQuantity>;
9
+ /** The last load or change failed (`tether-show="cart-error"`). */
10
+ failed: boolean;
11
+ }
12
+ /** @returns The quantity to show: the one asked for while an update is on its way. */
13
+ export declare const quantityOf: (line: ShopifyCartLine, pending: ReadonlyMap<string, PendingQuantity>) => number;
14
+ export declare function renderCart(view: CartView): void;
15
+ /** Shows the count a saved cart had on the last page in the cart counts the page left at 0, until it has loaded. */
16
+ export declare function showSavedCount(count: number): void;
17
+ export {};
@@ -0,0 +1,182 @@
1
+ import type { CartWarning } from '../config.js';
2
+ import type { Image } from '../fragments.js';
3
+ import type { Money } from '../shopify-values.js';
4
+ export interface Attribute {
5
+ key: string;
6
+ value: string;
7
+ }
8
+ /** A line's `merchandise`: Shopify's `Merchandise` union has only `ProductVariant`. */
9
+ export interface CartVariant {
10
+ id: string;
11
+ title: string;
12
+ sku: string | null;
13
+ availableForSale: boolean;
14
+ requiresShipping: boolean;
15
+ /** Absent when the store's token can't read inventory. */
16
+ quantityAvailable?: number | null;
17
+ image: Image | null;
18
+ price: Money;
19
+ compareAtPrice: Money | null;
20
+ selectedOptions: {
21
+ name: string;
22
+ value: string;
23
+ }[];
24
+ product: {
25
+ id: string;
26
+ title: string;
27
+ handle: string;
28
+ vendor: string;
29
+ featuredImage: Image | null;
30
+ };
31
+ }
32
+ interface LinePlanAllocation {
33
+ sellingPlan: {
34
+ id: string;
35
+ name: string;
36
+ description: string | null;
37
+ recurringDeliveries: boolean;
38
+ options: {
39
+ name: string;
40
+ value: string;
41
+ }[];
42
+ };
43
+ checkoutChargeAmount: Money;
44
+ priceAdjustments: {
45
+ price: Money;
46
+ compareAtPrice: Money;
47
+ perDeliveryPrice: Money;
48
+ }[];
49
+ }
50
+ export interface ShopifyCartLine {
51
+ id: string;
52
+ quantity: number;
53
+ attributes: Attribute[];
54
+ cost: {
55
+ totalAmount: Money;
56
+ subtotalAmount: Money;
57
+ amountPerQuantity: Money;
58
+ compareAtAmountPerQuantity: Money | null;
59
+ };
60
+ discountAllocations: {
61
+ discountedAmount: Money;
62
+ }[];
63
+ sellingPlanAllocation: LinePlanAllocation | null;
64
+ merchandise: CartVariant;
65
+ /** Only on a bundle (`ComponentizableCartLine`). */
66
+ lineComponents?: {
67
+ id: string;
68
+ quantity: number;
69
+ merchandise: {
70
+ id: string;
71
+ title: string;
72
+ product: {
73
+ title: string;
74
+ handle: string;
75
+ };
76
+ };
77
+ }[];
78
+ }
79
+ /** The fields of `fragment TetherCart`. */
80
+ export interface ShopifyCart {
81
+ id: string;
82
+ checkoutUrl: string;
83
+ totalQuantity: number;
84
+ note: string | null;
85
+ createdAt: string;
86
+ updatedAt: string;
87
+ attributes: Attribute[];
88
+ buyerIdentity: {
89
+ countryCode: string | null;
90
+ email: string | null;
91
+ };
92
+ discountCodes: {
93
+ code: string;
94
+ applicable: boolean;
95
+ }[];
96
+ cost: {
97
+ subtotalAmount: Money;
98
+ totalAmount: Money;
99
+ checkoutChargeAmount: Money;
100
+ };
101
+ lines: {
102
+ nodes: ShopifyCartLine[];
103
+ };
104
+ }
105
+ /**
106
+ * `unknown` until the saved cart has loaded (the page's own markup shows); `empty` without a cart (none saved, or
107
+ * Shopify no longer has it); `error` after a failed load or change, with the cart as Shopify last sent it.
108
+ */
109
+ export type CartStatus = {
110
+ kind: 'unknown';
111
+ } | {
112
+ kind: 'empty';
113
+ } | {
114
+ kind: 'ready';
115
+ cart: ShopifyCart;
116
+ } | {
117
+ kind: 'error';
118
+ error: unknown;
119
+ cart: ShopifyCart | null;
120
+ };
121
+ /** What one quick +/- click asked for; a newer click replaces it. */
122
+ export interface PendingQuantity {
123
+ quantity: number;
124
+ }
125
+ /** The second argument of a `subscribe` listener. */
126
+ interface CartMeta {
127
+ open: boolean;
128
+ count: number;
129
+ loaded: boolean;
130
+ /** A copy of Shopify's notes on the last change. */
131
+ warnings: CartWarning[];
132
+ }
133
+ /** Line id → what quick +/- clicks asked for, until Shopify has answered. */
134
+ export declare const pendingQuantities: Map<string, PendingQuantity>;
135
+ export declare const cartStatus: () => CartStatus;
136
+ /** The cart as Shopify last sent it. */
137
+ export declare function currentCart(): ShopifyCart | null;
138
+ export declare const currentWarnings: () => readonly CartWarning[];
139
+ /** The id of the visitor's cart, kept across pages. */
140
+ export declare const savedCartId: () => string | null;
141
+ /** Forgets the saved cart, which Shopify no longer has. */
142
+ export declare function forgetSavedCart(): void;
143
+ /** Keeps a cart Shopify sent without showing it, for an answer a newer change will replace. */
144
+ export declare function keepQuietly(cart: ShopifyCart | null): void;
145
+ /** Renders the document with the store's cart. */
146
+ export declare function render(): void;
147
+ /** @param reason The `reason` of `tether:cart-updated`. */
148
+ export declare function announce(reason: string): void;
149
+ /**
150
+ * Keeps a cart Shopify sent after a change, shows it, and tells this page and the visitor's other tabs.
151
+ *
152
+ * @returns `cart`.
153
+ */
154
+ export declare function commit(cart: ShopifyCart | null, reason: string): ShopifyCart | null;
155
+ /** The outcome of loading the saved cart: the cart, `null` when there is none, or the failure. */
156
+ export declare function settleLoad(outcome: {
157
+ kind: 'loaded';
158
+ cart: ShopifyCart | null;
159
+ } | {
160
+ kind: 'failed';
161
+ error: unknown;
162
+ }): void;
163
+ /** Until the saved cart has loaded, the counts show the count it had on the last page instead of the page's 0. */
164
+ export declare function showLastKnownCount(): void;
165
+ /** Sets Shopify's notes on the last change: each message once, in the site's words (`warningText`). */
166
+ export declare function setWarnings(notes: readonly CartWarning[]): void;
167
+ /** @returns A runner that starts each task once the one before has settled, whether it succeeded or failed. */
168
+ export declare function serial(): <T>(task: () => Promise<T>) => Promise<T>;
169
+ /**
170
+ * Runs a cart change after the ones before it, so quick clicks never race. A failure sets the `error` status (shown by
171
+ * `tether-show="cart-error"` until the next change succeeds), emits `tether:cart-error` and rejects.
172
+ */
173
+ export declare function enqueue<T>(task: () => Promise<T>): Promise<T>;
174
+ /** @returns The quantity to show for `line`. */
175
+ export declare const pendingQuantityOf: (line: ShopifyCartLine) => number;
176
+ /**
177
+ * Calls `listener` now and after every change of the cart or the drawer.
178
+ *
179
+ * @returns Stops listening.
180
+ */
181
+ export declare function subscribe(listener: (cart: ShopifyCart | null, meta: CartMeta) => void): () => void;
182
+ export {};
@@ -0,0 +1,106 @@
1
+ import { checkout, setCheckoutResolver } from './cart/checkout.js';
2
+ import { isOpen, openCart, closeCart, toggleCart, trapFocus } from './cart/drawer.js';
3
+ import { render, subscribe } from './cart/store.js';
4
+ import type { CartWarning } from './config.js';
5
+ import type { Attributes, LineSpec, LinePatch } from './cart/mutations.js';
6
+ import type { ShopifyCart, ShopifyCartLine, CartStatus } from './cart/store.js';
7
+ export { trapFocus };
8
+ type DiscountCodes = string[] | string | ((current: string[]) => string[]);
9
+ /**
10
+ * +1 / -1 for a line. Quick clicks add up: each builds on the quantity the click before asked for, which shows at once
11
+ * (`tether-line="quantity"`, `quantityOf`) until Shopify has answered. Only the latest target is sent; the ones it
12
+ * replaced are skipped.
13
+ */
14
+ declare function changeQuantity(lineId: string, delta: number): Promise<ShopifyCart | null>;
15
+ /**
16
+ * Loads the saved cart. A cart Shopify no longer has is forgotten; one it can't be asked about (the network, the
17
+ * token) is kept, because it is probably fine.
18
+ */
19
+ declare function init(): Promise<ShopifyCart | null>;
20
+ export declare const Cart: {
21
+ init: typeof init;
22
+ /** The saved cart's id. */
23
+ id: () => string | null;
24
+ readonly status: CartStatus;
25
+ readonly state: ShopifyCart | null;
26
+ /** The cart's lines, a copy: changing it changes nothing in the cart. */
27
+ readonly lines: ShopifyCartLine[];
28
+ readonly count: number;
29
+ /** True once the saved cart, or that there is none, is known. */
30
+ readonly loaded: boolean;
31
+ /**
32
+ * The failure of the last load or change, until a change succeeds. Requests fail with an `Error`; anything else
33
+ * thrown is wrapped, so the public type holds.
34
+ */
35
+ readonly lastError: Error | null;
36
+ /** Shopify's notes on the last change, a copy. */
37
+ readonly warnings: CartWarning[];
38
+ /** Line id → the quantity quick clicks asked for. */
39
+ readonly pendingQty: ReadonlyMap<string, {
40
+ quantity: number;
41
+ }>;
42
+ /**
43
+ * Sets where a logged-in customer checks out, or clears it with `null`: the account part's resolver answers the
44
+ * customer's checkout URL for the cart (`null` uses the cart's own).
45
+ */
46
+ setCheckoutResolver: typeof setCheckoutResolver;
47
+ /**
48
+ * Forgets the saved cart on this device, as if there were none: the page shows an empty cart and
49
+ * `tether:cart-updated` fires with reason `load`. The next add starts a new cart.
50
+ */
51
+ forgetSaved(): void;
52
+ render: typeof render;
53
+ subscribe: typeof subscribe;
54
+ /**
55
+ * `add(variantId, 2)`, `add({ variantId, quantity, sellingPlanId, attributes })`, or `add([line, line])` for
56
+ * bundles in one request. Creates the cart when there is none.
57
+ *
58
+ * @throws {TetherError} (rejected) When the add fails, or Shopify answers it without a cart.
59
+ */
60
+ add(input: string | number | LineSpec | (string | number | LineSpec)[], quantity?: number, options?: Omit<LineSpec, "variantId" | "quantity">): Promise<ShopifyCart>;
61
+ /**
62
+ * `updateLine(lineId, 3)` or `updateLine(lineId, { quantity, sellingPlanId, attributes })`.
63
+ *
64
+ * @param patch A quantity of 0 or less removes the line.
65
+ */
66
+ updateLine(lineId: string, patch: number | LinePatch): Promise<ShopifyCart | null>;
67
+ changeQuantity: typeof changeQuantity;
68
+ /** The quantity to show: the one asked for while an update is on its way. */
69
+ quantityOf: (line: ShopifyCartLine) => number;
70
+ removeLine(lineIds: string | string[]): Promise<ShopifyCart | null>;
71
+ clear(): Promise<ShopifyCart | null>;
72
+ setNote(note: string | null | undefined): Promise<ShopifyCart | null>;
73
+ /**
74
+ * Cart attributes, merged with the cart's (as it is when the change runs, so quick calls don't drop each other).
75
+ *
76
+ * @param attributes The value `''` removes a key.
77
+ */
78
+ setAttributes(attributes: Attributes): Promise<ShopifyCart | null>;
79
+ /** Replaces the discount codes; `[]` removes them all. */
80
+ setDiscountCodes(codes: DiscountCodes): Promise<ShopifyCart | null>;
81
+ /**
82
+ * Adds a code. A code Shopify doesn't accept is removed again, so it doesn't warn on every later change.
83
+ *
84
+ * @throws {TetherError} `INVALID_DISCOUNT` (rejected) when Shopify doesn't accept the code.
85
+ */
86
+ applyDiscount(code: string): Promise<ShopifyCart | null>;
87
+ removeDiscount(code: string): Promise<ShopifyCart | null>;
88
+ /**
89
+ * Sets who is buying; a country changes the cart's currency and prices to that market's.
90
+ *
91
+ * @param options `create: false` forgets an expired cart instead of creating an empty one.
92
+ */
93
+ setBuyerIdentity(buyerIdentity: {
94
+ countryCode?: string;
95
+ email?: string;
96
+ phone?: string;
97
+ }, { create }?: {
98
+ create?: boolean;
99
+ }): Promise<ShopifyCart | null>;
100
+ checkout: typeof checkout;
101
+ drawers: () => HTMLElement[];
102
+ isOpen: typeof isOpen;
103
+ open: typeof openCart;
104
+ close: typeof closeCart;
105
+ toggle: typeof toggleCart;
106
+ };
@@ -0,0 +1,100 @@
1
+ import type { TetherConfig } from './config.js';
2
+ export type TetherErrorCode = 'NOT_FOUND' | 'NETWORK' | 'NOT_CONFIGURED' | 'NOT_LOGGED_IN' | 'INVALID' | 'INVALID_COUNTRY' | 'INVALID_DISCOUNT' | 'ERROR' | `HTTP_${number}`;
3
+ export interface GraphQLError {
4
+ message: string;
5
+ extensions?: {
6
+ code?: string;
7
+ };
8
+ }
9
+ export interface UserError {
10
+ message: string;
11
+ field?: string[] | null;
12
+ code?: string | null;
13
+ }
14
+ interface TetherErrorDetails {
15
+ /** Default `HTTP_<status>` with a status, else `ERROR`. */
16
+ code?: TetherErrorCode;
17
+ /** The HTTP status of the response that failed. */
18
+ status?: number;
19
+ /** The GraphQL errors of that response. */
20
+ errors?: GraphQLError[];
21
+ /** A mutation's user errors. */
22
+ userErrors?: readonly UserError[];
23
+ }
24
+ export declare class TetherError extends Error {
25
+ name: "TetherError";
26
+ code: TetherErrorCode;
27
+ status: number | undefined;
28
+ errors: GraphQLError[] | undefined;
29
+ userErrors: readonly UserError[] | undefined;
30
+ constructor(message: string, { status, errors, userErrors, code }?: TetherErrorDetails);
31
+ }
32
+ /** What a request needs from a config: the browser's `cfg`, or a config the server helpers build. */
33
+ export type StorefrontConfig = Pick<TetherConfig, 'domain' | 'token' | 'apiVersion' | 'country' | 'language' | 'maxAge' | 'retries'> & {
34
+ fetch?: typeof fetch;
35
+ };
36
+ /** Checks a query's `data` before its caller reads it as a `T`. */
37
+ export type DataCheck<T> = (data: unknown) => data is T;
38
+ /**
39
+ * The structure of JSON a check accepts: a `typeof` name, `'object'` for a plain object, `[item]` for a list of
40
+ * items, an object's fields, or a check of its own. Fields it doesn't name aren't checked, so a shape names only what
41
+ * its reader walks without a fallback.
42
+ */
43
+ export type Shape = 'string' | 'number' | 'boolean' | 'object' | ((value: unknown) => boolean) | readonly [Shape] | {
44
+ readonly [key: string]: Shape;
45
+ };
46
+ /** @returns True when `value` has the structure `shape` describes. */
47
+ export declare function matches(value: unknown, shape: Shape): boolean;
48
+ /** @returns The shape of `null` or a value with the structure `shape` describes. */
49
+ export declare const orNull: (shape: Shape) => (value: unknown) => boolean;
50
+ /** @returns A check that `data` has the structure `shape` describes, which `T` states. */
51
+ export declare const shaped: <T>(shape: Shape) => DataCheck<T>;
52
+ /** `context: false` sends the query without `@inContext`. */
53
+ interface RequestOptions {
54
+ context?: boolean;
55
+ }
56
+ /** What the store's token may read beyond the defaults. */
57
+ export interface StoreAccess {
58
+ inventory: boolean;
59
+ }
60
+ /**
61
+ * A query's text, or a function that writes it for what the token may read: Tether's own queries, built from
62
+ * `fragments.ts`, leave out inventory for a token that can't read it.
63
+ */
64
+ export type QueryText = string | ((access: StoreAccess) => string);
65
+ /** @returns The Storefront GraphQL endpoint of `conf`'s store. */
66
+ export declare function endpoint(conf?: Pick<TetherConfig, 'domain' | 'apiVersion'>): string;
67
+ /**
68
+ * `inContext` for any country and language, as the public `Tether.withContext` takes them: one that isn't a valid
69
+ * code is left out, so page input never becomes query text.
70
+ */
71
+ export declare function withContext(query: string, ctx?: {
72
+ country?: string | undefined;
73
+ language?: string | undefined;
74
+ }): string;
75
+ /**
76
+ * One Storefront API request, for any config: the browser's shared one (`gql`) or one the server helpers build.
77
+ * Queries carry a time bucket so Shopify's cache of an identical query text is at most `maxAge` seconds old. A query
78
+ * written for the store's access leaves out inventory once the token turns out not to read it (`INVENTORY_FIELD`);
79
+ * a fixed query text is the caller's own, and that refusal is its error.
80
+ *
81
+ * @param isData Checks that `data` has what the caller then reads, so a malformed answer fails here.
82
+ * @throws {TetherError} `NOT_CONFIGURED` without a domain or token, `NETWORK` when the store can't be reached,
83
+ * `HTTP_<status>` for an HTTP or GraphQL failure, or for `data` that `isData` refuses.
84
+ */
85
+ export declare function request<T>(conf: StorefrontConfig, query: QueryText, variables: Record<string, unknown>, isData: DataCheck<T>, { context }?: RequestOptions): Promise<T>;
86
+ /** `request` with the browser's shared config. */
87
+ export declare function gql<T>(query: QueryText, variables: Record<string, unknown>, isData: DataCheck<T>, options?: RequestOptions): Promise<T>;
88
+ /** The check of `Tether.query()` and the server's `query()`, whose callers read `data` themselves. */
89
+ export declare const hasData: (data: unknown) => data is object;
90
+ /** `Tether.query()`: the caller's own query text decides the shape of `data`, and the caller's `T` states it. */
91
+ export declare function query<T>(text: QueryText, variables?: Record<string, unknown>, options?: RequestOptions): Promise<T>;
92
+ /**
93
+ * @param payload A mutation's payload.
94
+ * @param name The mutation, for the error message.
95
+ * @throws {TetherError} `ERROR` on an empty payload or on user errors (in `userErrors`).
96
+ */
97
+ export declare function unwrap<Payload extends {
98
+ userErrors?: readonly UserError[] | null;
99
+ }>(payload: Payload | null | undefined, name: string): Payload;
100
+ export {};
@@ -0,0 +1,25 @@
1
+ import type { Product } from './fragments.js';
2
+ import type { Collection, PageInfo, ProductQuery } from './loader.js';
3
+ export declare const listSelector: () => string;
4
+ export declare class ListScope {
5
+ readonly root: HTMLElement;
6
+ products: Product[];
7
+ pageInfo: PageInfo | null;
8
+ collection: Collection | null;
9
+ /** Counts loads, so only the latest one renders. */
10
+ loadToken: number;
11
+ constructor(root: HTMLElement);
12
+ $(selector: string): HTMLElement[];
13
+ /** The collection or search query of the list's attributes. */
14
+ options(): ProductQuery;
15
+ /**
16
+ * The product of a recommendations list: a handle, `url:…` (one dynamic product page), or empty inside a product
17
+ * scope for that product.
18
+ *
19
+ * @throws {TetherError} `NOT_FOUND` when there is no product to recommend for.
20
+ */
21
+ loadRecommendations(): Promise<Product[]>;
22
+ /** @param more Appends the next page instead of replacing the products. */
23
+ load(more?: boolean): Promise<void>;
24
+ render(): void;
25
+ }
@@ -0,0 +1,40 @@
1
+ import type { AccountConfig, AnalyticsConfig, MarketsConfig, MetafieldIdentifier, MetafieldRef } from './config.js';
2
+ /** Warns that an option was dropped. */
3
+ type Invalid = (name: string, value: unknown) => void;
4
+ /** A host name, without protocol or path: what `domainOption` keeps of the `domain` option. */
5
+ export type ShopDomain = string & {
6
+ readonly __brand: 'ShopDomain';
7
+ };
8
+ /** Every source `markets.detect` reads. */
9
+ export declare const MARKET_SOURCES: readonly ('saved' | 'server' | 'webflow')[];
10
+ /**
11
+ * @returns The store's domain without protocol or path (`https://shop.example/x` → `shop.example`), `''` for anything
12
+ * that isn't a host name.
13
+ */
14
+ export declare function domainOption(value: unknown, invalid: Invalid): ShopDomain | '';
15
+ /**
16
+ * The folder of the optional parts. It must be https or on this site, which keeps parts off plain http (mixed content,
17
+ * rewritable on the way); it doesn't vouch for the host, which the site chose.
18
+ *
19
+ * @returns The folder as written, `null` for none or one elsewhere (the parts then load beside the core).
20
+ */
21
+ export declare function partsUrlOption(value: unknown, invalid: Invalid): string | null;
22
+ /**
23
+ * The origins a site lets description iframes load from. Each one is trusted to show only what the site expects in a
24
+ * description, so only a bare https origin is taken: a path would suggest a narrower trust than is given.
25
+ *
26
+ * @returns The valid origins; each other entry is skipped with a warning.
27
+ */
28
+ export declare function embedOriginsOption(value: unknown, invalid: Invalid): string[];
29
+ /** @returns The identifier of `'namespace.key'` or `{ namespace, key }`, when both parts are valid identifiers. */
30
+ export declare function toMetafieldIdentifier(ref: unknown): MetafieldIdentifier | null;
31
+ /** @returns The refs that name a valid identifier, as written; each other one is skipped with a warning. */
32
+ export declare function metafieldsOption(value: unknown, invalid: Invalid): MetafieldRef[];
33
+ /**
34
+ * @param warn Says why an `account` without a usable `server` is off.
35
+ * @returns `null` without a usable `account.server`: nothing of the account part loads.
36
+ */
37
+ export declare function accountOption(value: unknown, invalid: Invalid, warn: (message: string) => void): AccountConfig | null;
38
+ export declare const marketsOption: (value: unknown, invalid: Invalid) => MarketsConfig | null;
39
+ export declare const analyticsOption: (value: unknown, invalid: Invalid) => AnalyticsConfig | null;
40
+ export {};
@@ -0,0 +1,185 @@
1
+ export { MARKET_SOURCES } from './config-sections.js';
2
+ import type { ApiVersion } from '../server/src/shared/api-versions.js';
3
+ import type { ShopDomain } from './config-sections.js';
4
+ import type { Money } from './shopify-values.js';
5
+ import type { CountryCode, LanguageCode } from './market-codes.js';
6
+ /** Where the visitor's market comes from (`markets.ts`). */
7
+ export interface MarketsConfig {
8
+ /** True: every source; a list: only those. */
9
+ detect?: boolean | ('saved' | 'server' | 'webflow')[];
10
+ /** Webflow locale (subdirectory or html lang) to market. */
11
+ locales?: Record<string, {
12
+ country?: string;
13
+ language?: string;
14
+ locale?: string;
15
+ }>;
16
+ /** The cookie a server sets with the visitor's country. */
17
+ cookie?: string;
18
+ /** Remember the visitor's choice (default true). */
19
+ remember?: boolean;
20
+ /** With `locales`: ignore an html lang that isn't in the map. */
21
+ strict?: boolean;
22
+ }
23
+ /** The optional analytics part. */
24
+ export interface AnalyticsConfig {
25
+ /** GA4/GTM events, into `window.dataLayer` or the named array. */
26
+ dataLayer?: boolean | string;
27
+ /** Shopify's own analytics. `proxy`: the site serves the Storefront API on its own domain. */
28
+ shopify?: boolean | {
29
+ consent?: 'no-banner' | 'default-banner' | 'custom-banner';
30
+ proxy?: boolean;
31
+ };
32
+ onEvent?: (name: string, data: Record<string, unknown>) => void;
33
+ }
34
+ /** The optional customer accounts part, which runs through the site's Worker. */
35
+ export interface AccountConfig {
36
+ /** The Worker's account routes, a URL or a path on this site. */
37
+ server: string;
38
+ /** The site's own order page, with `{id}` for the numeric order id. */
39
+ orderUrl?: string;
40
+ /** Shopify's subscription management page. */
41
+ subscriptionsUrl?: string;
42
+ /** A separate page for payment methods (default `subscriptionsUrl`). */
43
+ paymentUrl?: string;
44
+ /** Where Shopify sends the visitor after logout (default the home page). */
45
+ logoutUrl?: string;
46
+ /** The language of Shopify's login pages. */
47
+ locale?: string;
48
+ /** Replaces `location.assign` for the login redirect (popups, tests). */
49
+ navigate?: (url: string) => void;
50
+ }
51
+ export interface MetafieldIdentifier {
52
+ namespace: string;
53
+ key: string;
54
+ }
55
+ /** `'namespace.key'` or the identifier itself. */
56
+ export type MetafieldRef = string | MetafieldIdentifier;
57
+ /** Shopify's note on a cart change. */
58
+ export interface CartWarning {
59
+ code: string;
60
+ message: string;
61
+ target?: string;
62
+ }
63
+ export type CheckoutTarget = '_self' | '_blank';
64
+ type PurchaseKind = 'one-time' | 'subscription';
65
+ /** Every option, with its default filled in. */
66
+ export interface TetherConfig {
67
+ /** The store's domain, without protocol or path; `''` until one is set. */
68
+ domain: ShopDomain | '';
69
+ /** The public Storefront API access token. */
70
+ token: string;
71
+ /** A dated Shopify API version (or `unstable`, with a warning). */
72
+ apiVersion: ApiVersion | 'unstable';
73
+ /** The market's country, for `@inContext(country:)`. */
74
+ country: CountryCode | undefined;
75
+ /** The market's language, for `@inContext(language:)`. */
76
+ language: LanguageCode | undefined;
77
+ /** An `Intl` locale for money and dates. */
78
+ locale: string | undefined;
79
+ markets: MarketsConfig | null;
80
+ /** The attribute prefix: `tether` reads `tether-product`, `tether-bind`. */
81
+ prefix: string;
82
+ /** A folder (`/product`) or a template with `{handle}` / `{id}`. */
83
+ productPath: string;
84
+ collectionPath: string;
85
+ /** The product metafields to fetch. */
86
+ metafields: MetafieldRef[];
87
+ /** Origins (`https://video.example`) a description's iframes may load from, besides the built-in video players. */
88
+ embedOrigins: string[];
89
+ imagesFirst: number;
90
+ variantsFirst: number;
91
+ sellingPlanGroupsFirst: number;
92
+ sellingPlansFirst: number;
93
+ collectionFirst: number;
94
+ /** Retries of a request that failed on the network, a 429/5xx or Shopify's throttling. */
95
+ retries: number;
96
+ /** Refresh server-rendered products once in the background. */
97
+ revalidate: boolean;
98
+ /** The localStorage key of the cart id (default `<prefix>:cart:<domain>`). */
99
+ storageKey: string;
100
+ cartDrawerOpenClass: string;
101
+ openCartOnAdd: boolean;
102
+ /** Move focus into the open cart, keep Tab inside it, restore it on close. */
103
+ cartFocus: boolean;
104
+ checkoutTarget: CheckoutTarget;
105
+ defaultPurchase: PurchaseKind;
106
+ /** `?variant=` follows the visitor's choice on the page's main product. */
107
+ variantUrl: boolean;
108
+ /** Seconds a Shopify answer may be cached; 0 leaves it to Shopify, which can be hours. */
109
+ maxAge: number;
110
+ /** `tether-show="low-stock"` at this many or fewer (needs inventory access). */
111
+ lowStock: number;
112
+ /** Milliseconds the search part waits after the last keystroke. */
113
+ searchDelay: number;
114
+ /** Mount the document on DOMContentLoaded. */
115
+ autoMount: boolean;
116
+ /** Mount elements added later (CMS load more, filters). */
117
+ observe: boolean;
118
+ /** Mount on Astro's `astro:page-load` and close the cart on navigation. */
119
+ astro: boolean;
120
+ /** The folder of the optional parts, for the `<script>` build. */
121
+ partsUrl: string | null;
122
+ analytics: AnalyticsConfig | null;
123
+ account: AccountConfig | null;
124
+ /** The site's text for a cart warning; nothing keeps Shopify's. */
125
+ warningText: ((warning: CartWarning) => string | null | undefined) | null;
126
+ formatMoney: ((money: Money, locale: string | undefined) => string) | null;
127
+ /** Default: `ScrollTrigger.refresh()` when present. */
128
+ onLayoutChange: ((reason: string) => void) | null;
129
+ onCartOpen: ((drawers: HTMLElement[]) => void) | null;
130
+ onCartClose: ((drawers: HTMLElement[]) => void) | null;
131
+ onLineEnter: ((node: HTMLElement) => void) | null;
132
+ /** Animates a line out, then calls `done()`. */
133
+ onLineLeave: ((node: HTMLElement, done: () => void) => void) | null;
134
+ onValueChange: ((element: HTMLElement, previousText: string) => void) | null;
135
+ /** Every failure that reaches the visitor. */
136
+ onError: ((error: unknown, context: string) => void) | null;
137
+ debug: boolean;
138
+ }
139
+ /** The options a site writes as plain text, which `resolveConfig` checks into their typed form. */
140
+ type ParsedFromText = 'domain' | 'apiVersion' | 'country' | 'language' | 'storageKey';
141
+ /**
142
+ * What a site passes: any option, the market as the codes people write (`'de'`, `'pt-BR'`), and `null` for the
143
+ * default storage key.
144
+ */
145
+ export type TetherOptions = {
146
+ [Key in Exclude<keyof TetherConfig, ParsedFromText>]?: TetherConfig[Key];
147
+ } & Partial<Record<Exclude<ParsedFromText, 'storageKey'>, string | undefined>> & {
148
+ storageKey?: string | null | undefined;
149
+ };
150
+ export declare const DEFAULTS: Readonly<TetherConfig>;
151
+ export declare const cfg: TetherConfig;
152
+ /**
153
+ * `cfg` as `Tether.config` shows it. A value written there would skip the validation `configure()` does, so the view
154
+ * is read-only; `Tether.init(options)` is how a site changes the config.
155
+ */
156
+ export declare const configView: TetherConfig;
157
+ /**
158
+ * The configuration `options` describe: defaults filled in, every value validated (an invalid one warns and falls
159
+ * back to its default), hooks that aren't functions dropped so callers only check for null.
160
+ *
161
+ * @param options What the site set, or any object (the server helpers take their options from code and `.env`).
162
+ */
163
+ export declare function resolveConfig(options: TetherOptions | Readonly<Record<string, unknown>>): TetherConfig;
164
+ /**
165
+ * @param value A `markets` option from code other than the config (`Tether.detectMarket(markets)`).
166
+ * @returns It validated as the config's own is, with the same warnings.
167
+ */
168
+ export declare const parseMarkets: (value: unknown) => MarketsConfig | null;
169
+ /**
170
+ * Replaces the shared configuration with `options` over the defaults (see `resolveConfig`).
171
+ *
172
+ * @returns The shared `cfg`.
173
+ */
174
+ export declare function configure(options?: TetherOptions | Readonly<Record<string, unknown>>): TetherConfig;
175
+ /** @returns True once a domain and a token are set. */
176
+ export declare const isConfigured: () => boolean;
177
+ export declare function log(...args: unknown[]): void;
178
+ export declare function warn(...args: unknown[]): void;
179
+ /**
180
+ * Logs a failure that reaches the visitor and hands it to `onError`. A throwing `onError` is logged, never rethrown:
181
+ * the site's hook must not break the render that reported the failure.
182
+ *
183
+ * @param context What failed, e.g. `product coffee-box`.
184
+ */
185
+ export declare function reportError(error: unknown, context: string): void;
@@ -0,0 +1,2 @@
1
+ /** Binds the document-wide listeners, once per page load of the script. */
2
+ export declare function bindCartControls(): void;
@@ -0,0 +1,2 @@
1
+ /** Binds the document-wide listeners, once per page load of the script. */
2
+ export declare function bindDiscountControls(): void;
@@ -0,0 +1,5 @@
1
+ import type { ShopifyCart } from '../cart/store.js';
2
+ /** `[tether-group]` with `[tether-group-add]`: adds every product scope inside the group in one request. */
3
+ export declare const addGroup: (button: Element) => Promise<ShopifyCart | null>;
4
+ /** Binds the document-wide listeners, once per page load of the script. */
5
+ export declare function bindExtrasControls(): void;