@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
@@ -3,43 +3,25 @@
3
3
  * (`tether-discount-remove` inside `tether-discount-code`), delegated from the document. A code Shopify refuses marks
4
4
  * the form `is-invalid` (and the input `aria-invalid`) until the visitor types again.
5
5
  */
6
- import { reportError } from '../config.js';
7
- import { TetherError } from '../client.js';
8
- import { Cart } from '../cart.js';
9
- import { attr, sel, busy, emit } from '../dom.js';
6
+ import { reportError } from '../config.ts';
7
+ import { TetherError } from '../client.ts';
8
+ import { Cart } from '../cart.ts';
9
+ import { attr, sel, busy, emit } from '../dom.ts';
10
10
 
11
- /**
12
- * @param {EventTarget | null} target
13
- * @returns {target is Element}
14
- */
15
- const isElement = (target) => target instanceof window.Element;
11
+ const isElement = (target: EventTarget | null): target is Element => target instanceof window.Element;
16
12
 
17
- /**
18
- * @param {Element} form
19
- * @returns {HTMLInputElement | null}
20
- */
21
- function codeInputOf(form) {
13
+ function codeInputOf(form: Element): HTMLInputElement | null {
22
14
  const input = form.querySelector(sel('discount-input')) ?? form.querySelector('input');
23
15
  return input instanceof window.HTMLInputElement ? input : null;
24
16
  }
25
17
 
26
- /**
27
- * @param {Element} form
28
- * @param {HTMLInputElement | null} input
29
- * @param {boolean} invalid
30
- */
31
- function markInvalid(form, input, invalid) {
18
+ function markInvalid(form: Element, input: HTMLInputElement | null, invalid: boolean): void {
32
19
  form.classList.toggle('is-invalid', invalid);
33
20
  if (invalid) input?.setAttribute('aria-invalid', 'true');
34
21
  else input?.removeAttribute('aria-invalid');
35
22
  }
36
23
 
37
- /**
38
- * @param {Element} form
39
- * @param {HTMLInputElement | null} input
40
- * @param {string} code
41
- */
42
- async function applyCode(form, input, code) {
24
+ async function applyCode(form: Element, input: HTMLInputElement | null, code: string): Promise<void> {
43
25
  try {
44
26
  await Cart.applyDiscount(code);
45
27
  markInvalid(form, input, false);
@@ -52,8 +34,7 @@ async function applyCode(form, input, code) {
52
34
  }
53
35
  }
54
36
 
55
- /** @param {SubmitEvent} event */
56
- function onSubmit(event) {
37
+ function onSubmit(event: SubmitEvent): void {
57
38
  const { target } = event;
58
39
  const form = isElement(target) ? target.closest(sel('discount-form')) : null;
59
40
  if (!form) return;
@@ -64,8 +45,7 @@ function onSubmit(event) {
64
45
  void busy(form, () => applyCode(form, input, code));
65
46
  }
66
47
 
67
- /** @param {Event} event */
68
- function onInput(event) {
48
+ function onInput(event: Event): void {
69
49
  const { target } = event;
70
50
  if (!isElement(target)) return;
71
51
  const form = target.closest(sel('discount-form'));
@@ -74,21 +54,20 @@ function onInput(event) {
74
54
  target.removeAttribute('aria-invalid');
75
55
  }
76
56
 
77
- /** @param {MouseEvent} event */
78
- function onClick(event) {
57
+ function onClick(event: MouseEvent): void {
79
58
  const { target } = event;
80
59
  const remove = isElement(target) ? target.closest(sel('discount-remove')) : null;
81
60
  if (!remove) return;
82
61
  event.preventDefault();
83
62
  const code = remove.closest(sel('discount-code'))?.getAttribute(attr('discount-code'));
84
63
  if (!code) return;
85
- Cart.removeDiscount(code).catch((/** @type {unknown} */ error) => {
64
+ Cart.removeDiscount(code).catch((error: unknown) => {
86
65
  reportError(error, 'discount');
87
66
  });
88
67
  }
89
68
 
90
69
  /** Binds the document-wide listeners, once per page load of the script. */
91
- export function bindDiscountControls() {
70
+ export function bindDiscountControls(): void {
92
71
  // Capture: the submit is handled before a router (Astro's ClientRouter) turns it into a navigation.
93
72
  document.addEventListener('submit', onSubmit, true);
94
73
  document.addEventListener('input', onInput);
@@ -3,28 +3,20 @@
3
3
  * group add buttons, the cart or market changing in another tab, and shareable variant links. A part that can't load
4
4
  * (a self-hosted site without `tether.extras.min.js`) is reported, so the missing file shows.
5
5
  */
6
- import { cfg, reportError } from '../config.js';
7
- import { attr, sel, busy } from '../dom.js';
8
- import { loadPart } from '../lazy.js';
9
- /** @import { ShopifyCart } from '../cart/store.js' */
6
+ import { cfg, reportError } from '../config.ts';
7
+ import { attr, sel, busy, isOwnEvent } from '../dom.ts';
8
+ import { loadPart } from '../lazy.ts';
9
+ import type { ShopifyCart } from '../cart/store.ts';
10
10
 
11
- /** @typedef {Parameters<typeof import('../extras.js').writeVariantUrl>[1]} VariantChoice */
11
+ /** `[tether-group]` with `[tether-group-add]`: adds every product scope inside the group in one request. */
12
+ export const addGroup = (button: Element): Promise<ShopifyCart | null> =>
13
+ loadPart('extras').then((part) => part.addGroup(button));
12
14
 
13
- /**
14
- * `[tether-group]` with `[tether-group-add]`: adds every product scope inside the group in one request.
15
- *
16
- * @param {Element} button
17
- * @returns {Promise<ShopifyCart | null>}
18
- */
19
- export const addGroup = (button) => loadPart('extras').then((part) => part.addGroup(button));
20
-
21
- /** @param {unknown} error */
22
- const reportExtrasError = (error) => {
15
+ const reportExtrasError = (error: unknown) => {
23
16
  reportError(error, 'extras');
24
17
  };
25
18
 
26
- /** @param {MouseEvent} event */
27
- function onClick(event) {
19
+ function onClick(event: MouseEvent): void {
28
20
  const { target } = event;
29
21
  const button = target instanceof window.Element ? target.closest(sel('group-add')) : null;
30
22
  if (!button) return;
@@ -32,8 +24,7 @@ function onClick(event) {
32
24
  busy(button, () => addGroup(button)).catch(reportExtrasError);
33
25
  }
34
26
 
35
- /** @param {StorageEvent} event */
36
- function onStorage(event) {
27
+ function onStorage(event: StorageEvent): void {
37
28
  if (!cfg.domain || !event.key?.startsWith(`${cfg.prefix}:`)) return;
38
29
  loadPart('extras')
39
30
  .then((part) => {
@@ -42,8 +33,9 @@ function onStorage(event) {
42
33
  .catch(reportExtrasError);
43
34
  }
44
35
 
45
- /** @param {CustomEvent<VariantChoice>} event `tether:variant-changed`, which a product scope emits. */
46
- function onVariantChanged(event) {
36
+ /** @param event `tether:variant-changed`, which a product scope emits; one another script dispatches is ignored. */
37
+ function onVariantChanged(event: Event): void {
38
+ if (!isOwnEvent(event, 'variant-changed')) return;
47
39
  const { target } = event;
48
40
  if (!(target instanceof window.Element)) return;
49
41
  if (!cfg.variantUrl && !target.hasAttribute(attr('variant-url'))) return;
@@ -55,9 +47,8 @@ function onVariantChanged(event) {
55
47
  }
56
48
 
57
49
  /** Binds the document-wide listeners, once per page load of the script. */
58
- export function bindExtrasControls() {
50
+ export function bindExtrasControls(): void {
59
51
  document.addEventListener('click', onClick);
60
52
  window.addEventListener('storage', onStorage);
61
- // Only product scopes emit this event, always with a CustomEvent.
62
- document.addEventListener(`${cfg.prefix}:variant-changed`, /** @type {EventListener} */ (onVariantChanged));
53
+ document.addEventListener(`${cfg.prefix}:variant-changed`, onVariantChanged);
63
54
  }
@@ -3,31 +3,29 @@
3
3
  * `astro:before-swap`) mounts each new page in its market and closes the cart while leaving; a country select
4
4
  * switches the market; back online, what failed to load loads again.
5
5
  */
6
- import { cfg, reportError } from '../config.js';
7
- import { attr, sel } from '../dom.js';
8
- import { liveLists, retryFailed } from '../mounting.js';
9
- import { marketForArrival, setCountry } from '../page-market.js';
6
+ import { cfg, reportError } from '../config.ts';
7
+ import { attr, sel } from '../dom.ts';
8
+ import { liveLists, retryFailed } from '../mounting.ts';
9
+ import { marketForArrival, setCountry } from '../page-market.ts';
10
10
 
11
- /**
12
- * @typedef {object} PageHooks What the entry module does for a page.
13
- * @property {() => Promise<unknown>} ready Settles once `init()` has applied the visitor's market.
14
- * @property {(root: Document) => unknown} mount
15
- * @property {() => void} unmount
16
- */
11
+ /** What the entry module does for a page. */
12
+ interface PageHooks {
13
+ /** Settles once `init()` has applied the visitor's market. */
14
+ ready: () => Promise<unknown>;
15
+ mount: (root: Document) => unknown;
16
+ unmount: () => void;
17
+ }
17
18
 
18
- /**
19
- * The body the last full-document mount ran on: Astro's first `astro:page-load` comes after that mount.
20
- *
21
- * @type {HTMLElement | null} */
22
- let mountedBody = null;
19
+ /** The body the last full-document mount ran on: Astro's first `astro:page-load` comes after that mount. */
20
+ let mountedBody: HTMLElement | null = null;
23
21
 
24
22
  /** Records a mount of the whole document, so the `astro:page-load` of the same page sends no second round of requests. */
25
- export function rememberDocumentMount() {
23
+ export function rememberDocumentMount(): void {
26
24
  mountedBody = document.body;
27
25
  }
28
26
 
29
- /** @param {PageHooks} hooks */
30
- async function onPageLoad(hooks) {
27
+ /** Mounts the page Astro swapped in. It rejects when `init()` failed or the mount threw; the listener reports that. */
28
+ async function onPageLoad(hooks: PageHooks): Promise<void> {
31
29
  await hooks.ready();
32
30
  const target = marketForArrival();
33
31
  if (!target) {
@@ -42,10 +40,11 @@ async function onPageLoad(hooks) {
42
40
  hooks.mount(document);
43
41
  }
44
42
 
45
- /** @param {PageHooks} hooks */
46
- function bindAstroRouter(hooks) {
43
+ function bindAstroRouter(hooks: PageHooks): void {
47
44
  document.addEventListener('astro:page-load', () => {
48
- void onPageLoad(hooks);
45
+ onPageLoad(hooks).catch((error: unknown) => {
46
+ reportError(error, 'page mount');
47
+ });
49
48
  });
50
49
  document.addEventListener('astro:before-swap', () => {
51
50
  mountedBody = null;
@@ -53,12 +52,11 @@ function bindAstroRouter(hooks) {
53
52
  });
54
53
  }
55
54
 
56
- /** @param {Event} event */
57
- function onChange(event) {
55
+ function onChange(event: Event): void {
58
56
  const { target } = event;
59
57
  if (!(target instanceof window.HTMLSelectElement) || !target.matches(sel('country-select'))) return;
60
58
  if (!target.value) return;
61
- setCountry(target.value).catch((/** @type {unknown} */ error) => {
59
+ setCountry(target.value).catch((error: unknown) => {
62
60
  reportError(error, 'country');
63
61
  });
64
62
  }
@@ -71,12 +69,8 @@ function onOnline() {
71
69
  });
72
70
  }
73
71
 
74
- /**
75
- * Binds the document-wide listeners, once per page load of the script.
76
- *
77
- * @param {PageHooks} hooks
78
- */
79
- export function bindNavigation(hooks) {
72
+ /** Binds the document-wide listeners, once per page load of the script. */
73
+ export function bindNavigation(hooks: PageHooks): void {
80
74
  if (cfg.astro) bindAstroRouter(hooks);
81
75
  document.addEventListener('change', onChange);
82
76
  window.addEventListener('online', onOnline);
@@ -2,19 +2,22 @@
2
2
  * The markup half of `Tether.diagnose()`: problems that show without the network. It knows every value and attribute
3
3
  * name Tether reads, so a typo gets a "did you mean".
4
4
  */
5
- import { cfg } from './config.js';
6
- import { attr, sel, get, qsa, hasTemplate } from './dom.js';
7
- import { resolveProductKey, boundary } from './status.js';
5
+ import { cfg } from './config.ts';
6
+ import { attr, sel, get, qsa, hasTemplate } from './dom.ts';
7
+ import { resolveProductKey, boundary } from './status.ts';
8
8
 
9
- /** @typedef {'error' | 'warn'} ProblemLevel */
10
- /**
11
- * @typedef {object} Problem
12
- * @property {ProblemLevel} level
13
- * @property {string} message
14
- * @property {string} [element] The element, described for people: `<div#id.class>`.
15
- * @property {Element} [el] The element itself, for the console.
16
- */
17
- /** @typedef {(level: ProblemLevel, message: string, el?: Element) => void} AddProblem */
9
+ type ProblemLevel = 'error' | 'warn';
10
+
11
+ export interface Problem {
12
+ level: ProblemLevel;
13
+ message: string;
14
+ /** The element, described for people: `<div#id.class>`. */
15
+ element?: string;
16
+ /** The element itself, for the console. */
17
+ el?: Element;
18
+ }
19
+
20
+ type AddProblem = (level: ProblemLevel, message: string, el?: Element) => void;
18
21
 
19
22
  /** Every `tether-show` value. */
20
23
  const SHOW = new Set([
@@ -139,14 +142,9 @@ const IMAGE_BIND = /^image:\d+$/;
139
142
  /** The class names of an element that its description shows. */
140
143
  const DESCRIBED_CLASSES = 2;
141
144
 
142
- /**
143
- * @param {string} from
144
- * @param {string} to
145
- * @returns {number} The Levenshtein distance: the fewest single-character edits from one to the other.
146
- */
147
- function editDistance(from, to) {
148
- /** @type {number[]} */
149
- let previous = Array.from({ length: to.length + 1 }, (_, index) => index);
145
+ /** The Levenshtein distance: the fewest single-character edits from one string to the other. */
146
+ function editDistance(from: string, to: string): number {
147
+ let previous: number[] = Array.from({ length: to.length + 1 }, (_, index) => index);
150
148
  for (let i = 1; i <= from.length; i++) {
151
149
  const row = [i];
152
150
  for (let j = 1; j <= to.length; j++) {
@@ -159,14 +157,13 @@ function editDistance(from, to) {
159
157
  }
160
158
 
161
159
  /**
162
- * @param {string} value What the markup says.
163
- * @param {Iterable<string>} options What Tether reads.
164
- * @param {string} [prefix] Put before the suggestion, e.g. `tether-`.
165
- * @returns {string} ` Did you mean "…"?` for an option close enough to `value`, else `''`.
160
+ * @param value What the markup says.
161
+ * @param options What Tether reads.
162
+ * @param prefix Put before the suggestion, e.g. `tether-`.
163
+ * @returns ` Did you mean "…"?` for an option close enough to `value`, else `''`.
166
164
  */
167
- export function didYouMean(value, options, prefix = '') {
168
- /** @type {string | null} */
169
- let best = null;
165
+ export function didYouMean(value: string, options: Iterable<string>, prefix = ''): string {
166
+ let best: string | null = null;
170
167
  let bestDistance = Infinity;
171
168
  for (const option of options) {
172
169
  const distance = editDistance(value, option);
@@ -179,11 +176,8 @@ export function didYouMean(value, options, prefix = '') {
179
176
  return best !== null && bestDistance <= closeEnough ? ` Did you mean "${prefix}${best}"?` : '';
180
177
  }
181
178
 
182
- /**
183
- * @param {Element} el
184
- * @returns {string} `<div#id.first.second>`.
185
- */
186
- function describe(el) {
179
+ /** `el` described for people: `<div#id.first.second>`. */
180
+ function describe(el: Element): string {
187
181
  const id = el.id ? `#${el.id}` : '';
188
182
  const classes = el.className && typeof el.className === 'string' ? el.className.trim().split(/\s+/) : [];
189
183
  const classText = classes.length ? `.${classes.slice(0, DESCRIBED_CLASSES).join('.')}` : '';
@@ -192,11 +186,7 @@ function describe(el) {
192
186
 
193
187
  const listSelector = () => `${sel('collection')},${sel('products')},${sel('recommendations')}`;
194
188
 
195
- /**
196
- * @param {ParentNode} root
197
- * @param {AddProblem} add
198
- */
199
- function checkProductKeys(root, add) {
189
+ function checkProductKeys(root: ParentNode, add: AddProblem): void {
200
190
  qsa(root, sel('product')).forEach((el) => {
201
191
  // A list card gets its handle when the list renders.
202
192
  if (el.closest(listSelector()) && el.closest(sel('list'))) return;
@@ -213,11 +203,7 @@ function checkProductKeys(root, add) {
213
203
  });
214
204
  }
215
205
 
216
- /**
217
- * @param {ParentNode} root
218
- * @param {AddProblem} add
219
- */
220
- function checkPlacement(root, add) {
206
+ function checkPlacement(root: ParentNode, add: AddProblem): void {
221
207
  for (const name of PRODUCT_ONLY) {
222
208
  qsa(root, sel(name)).forEach((el) => {
223
209
  if (el.closest(sel('product'))) return;
@@ -242,11 +228,7 @@ function checkPlacement(root, add) {
242
228
  });
243
229
  }
244
230
 
245
- /**
246
- * @param {ParentNode} root
247
- * @param {AddProblem} add
248
- */
249
- function checkValues(root, add) {
231
+ function checkValues(root: ParentNode, add: AddProblem): void {
250
232
  qsa(root, sel('show')).forEach((el) => {
251
233
  const value = get(el, 'show') ?? '';
252
234
  if (!SHOW.has(value)) add('warn', `Unknown ${attr('show')}="${value}".${didYouMean(value, SHOW)}`, el);
@@ -258,11 +240,7 @@ function checkValues(root, add) {
258
240
  });
259
241
  }
260
242
 
261
- /**
262
- * @param {ParentNode} root
263
- * @param {AddProblem} add
264
- */
265
- function checkMetafieldBinds(root, add) {
243
+ function checkMetafieldBinds(root: ParentNode, add: AddProblem): void {
266
244
  qsa(root, sel('bind')).forEach((el) => {
267
245
  const value = get(el, 'bind') || '';
268
246
  if (!value.startsWith(METAFIELD_BIND)) return;
@@ -278,14 +256,10 @@ function checkMetafieldBinds(root, add) {
278
256
 
279
257
  /**
280
258
  * Typos in attribute names (tether-bnd, tether-add-to-card) do nothing, silently. Each unknown name is reported once.
281
- *
282
- * @param {ParentNode} root
283
- * @param {AddProblem} add
284
259
  */
285
- function checkAttributeNames(root, add) {
260
+ function checkAttributeNames(root: ParentNode, add: AddProblem): void {
286
261
  const prefix = `${cfg.prefix}-`;
287
- /** @type {Set<string>} */
288
- const reported = new Set();
262
+ const reported = new Set<string>();
289
263
  qsa(root, '*').forEach((el) => {
290
264
  for (const { name: fullName } of el.attributes) {
291
265
  if (!fullName.startsWith(prefix)) continue;
@@ -297,11 +271,7 @@ function checkAttributeNames(root, add) {
297
271
  });
298
272
  }
299
273
 
300
- /**
301
- * @param {ParentNode} root
302
- * @param {AddProblem} add
303
- */
304
- function checkCartAndScopes(root, add) {
274
+ function checkCartAndScopes(root: ParentNode, add: AddProblem): void {
305
275
  const drawers = qsa(root, sel('cart-drawer')).length;
306
276
  if (qsa(root, sel('cart-open')).length && !drawers) {
307
277
  add('warn', `${attr('cart-open')} exists but there is no ${attr('cart-drawer')} on this page.`);
@@ -312,15 +282,10 @@ function checkCartAndScopes(root, add) {
312
282
  });
313
283
  }
314
284
 
315
- /**
316
- * @param {ParentNode} [root]
317
- * @returns {Problem[]} The markup problems in `root`, in page order per check.
318
- */
319
- export function checkMarkup(root = document) {
320
- /** @type {Problem[]} */
321
- const problems = [];
322
- /** @type {AddProblem} */
323
- const add = (level, message, el) => {
285
+ /** The markup problems in `root`, in page order per check. */
286
+ export function checkMarkup(root: ParentNode = document): Problem[] {
287
+ const problems: Problem[] = [];
288
+ const add: AddProblem = (level, message, el) => {
324
289
  problems.push(el ? { level, message, element: describe(el), el } : { level, message });
325
290
  };
326
291
  checkProductKeys(root, add);