@occluze/tether 0.6.7 → 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 (250) 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 -1
  24. package/dist/presets/barba.esm.js +55 -39
  25. package/dist/tether.account-pages.js +781 -495
  26. package/dist/tether.account-pages.min.js +1 -1
  27. package/dist/tether.account.js +462 -381
  28. package/dist/tether.account.min.js +1 -1
  29. package/dist/tether.analytics.js +299 -194
  30. package/dist/tether.analytics.min.js +1 -1
  31. package/dist/tether.diagnose.js +354 -262
  32. package/dist/tether.diagnose.min.js +2 -2
  33. package/dist/tether.extras.js +82 -45
  34. package/dist/tether.extras.min.js +1 -1
  35. package/dist/tether.js +3653 -2116
  36. package/dist/tether.markets.js +238 -150
  37. package/dist/tether.markets.min.js +1 -1
  38. package/dist/tether.min.js +3 -3
  39. package/dist/tether.min.js.map +4 -4
  40. package/dist/tether.search.js +207 -136
  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 +42 -22
  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.ts +75 -0
  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.ts +253 -0
  127. package/src/account/server-client.ts +183 -0
  128. package/src/account/subscriptions-view.ts +115 -0
  129. package/src/account-api.ts +98 -0
  130. package/src/account-marks.ts +35 -0
  131. package/src/account-pages.ts +81 -0
  132. package/src/account.ts +284 -0
  133. package/src/amounts.ts +64 -0
  134. package/src/analytics-shopify.ts +246 -0
  135. package/src/analytics.ts +315 -0
  136. package/src/api-version.ts +79 -0
  137. package/src/astro.ts +272 -0
  138. package/src/cart/cart-shape.ts +25 -0
  139. package/src/cart/checkout.ts +101 -0
  140. package/src/cart/drawer.ts +125 -0
  141. package/src/cart/mutations.ts +237 -0
  142. package/src/cart/render.ts +266 -0
  143. package/src/cart/store.ts +279 -0
  144. package/src/cart.ts +345 -0
  145. package/src/client.ts +391 -0
  146. package/src/collection.ts +175 -0
  147. package/src/config-sections.ts +284 -0
  148. package/src/config.ts +480 -0
  149. package/src/controls/cart-controls.ts +123 -0
  150. package/src/controls/discount.ts +75 -0
  151. package/src/controls/extras.ts +54 -0
  152. package/src/controls/navigation.ts +77 -0
  153. package/src/diagnose-markup.ts +298 -0
  154. package/src/diagnose.ts +294 -0
  155. package/src/dom.ts +268 -0
  156. package/src/extras.ts +136 -0
  157. package/src/fragments.ts +261 -0
  158. package/src/globals.d.ts +15 -0
  159. package/src/images.ts +91 -0
  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.ts +363 -0
  165. package/src/market-codes.ts +6 -0
  166. package/src/markets-ui.ts +227 -0
  167. package/src/markets.ts +246 -0
  168. package/src/markup.ts +127 -0
  169. package/src/mounting.ts +264 -0
  170. package/src/page-market.ts +110 -0
  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.ts +115 -0
  181. package/src/product-render.ts +218 -0
  182. package/src/product-shape.ts +49 -0
  183. package/src/product-state.ts +318 -0
  184. package/src/product.ts +308 -0
  185. package/src/repeaters.ts +161 -0
  186. package/src/search.ts +305 -0
  187. package/src/selling-plans.ts +145 -0
  188. package/src/server.ts +211 -0
  189. package/src/shopify-values.ts +91 -0
  190. package/src/status.ts +106 -0
  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.ts +144 -0
  195. package/src/variant-render.ts +139 -0
  196. package/dist/esm/chunks/account-Q6E2TFEV.js +0 -1
  197. package/dist/esm/chunks/account-pages-UZXR4R5F.js +0 -1
  198. package/dist/esm/chunks/analytics-IJBKATUP.js +0 -1
  199. package/dist/esm/chunks/chunk-4QJKLPYR.js +0 -1
  200. package/dist/esm/chunks/chunk-64TUD3QR.js +0 -1
  201. package/dist/esm/chunks/chunk-AJLPU2JC.js +0 -4
  202. package/dist/esm/chunks/chunk-FIOZPPC5.js +0 -1
  203. package/dist/esm/chunks/chunk-K2YEQAIH.js +0 -1
  204. package/dist/esm/chunks/chunk-KIDH76B7.js +0 -1
  205. package/dist/esm/chunks/chunk-PAYT5ZD7.js +0 -1
  206. package/dist/esm/chunks/chunk-RREHGH4A.js +0 -1
  207. package/dist/esm/chunks/chunk-SSJT43CJ.js +0 -1
  208. package/dist/esm/chunks/chunk-VC46IEJQ.js +0 -1
  209. package/dist/esm/chunks/diagnose-3WVAWNTX.js +0 -14
  210. package/dist/esm/chunks/extras-7I7WJX35.js +0 -1
  211. package/dist/esm/chunks/markets-ui-545XLFTE.js +0 -1
  212. package/dist/esm/chunks/search-NILMGYXG.js +0 -1
  213. package/src/account-pages.js +0 -318
  214. package/src/account.js +0 -385
  215. package/src/analytics.js +0 -232
  216. package/src/api-version.js +0 -35
  217. package/src/astro.js +0 -101
  218. package/src/cart.js +0 -559
  219. package/src/client.js +0 -131
  220. package/src/collection.js +0 -114
  221. package/src/config.js +0 -85
  222. package/src/diagnose.js +0 -246
  223. package/src/dom.js +0 -288
  224. package/src/extras.js +0 -63
  225. package/src/fragments.js +0 -144
  226. package/src/index.js +0 -395
  227. package/src/initial.js +0 -75
  228. package/src/lazy-iife.js +0 -46
  229. package/src/lazy.js +0 -18
  230. package/src/loader.js +0 -198
  231. package/src/markets-ui.js +0 -90
  232. package/src/markets.js +0 -133
  233. package/src/mounting.js +0 -154
  234. package/src/parts/account-pages.js +0 -3
  235. package/src/parts/account.js +0 -3
  236. package/src/parts/analytics.js +0 -3
  237. package/src/parts/diagnose.js +0 -3
  238. package/src/parts/extras.js +0 -3
  239. package/src/parts/markets.js +0 -3
  240. package/src/parts/search.js +0 -3
  241. package/src/presets/barba.js +0 -100
  242. package/src/product.js +0 -570
  243. package/src/search.js +0 -153
  244. package/src/selling-plans.js +0 -94
  245. package/src/server.js +0 -252
  246. package/src/status.js +0 -66
  247. package/types/astro.d.ts +0 -17
  248. package/types/barba.d.ts +0 -16
  249. package/types/index.d.ts +0 -426
  250. package/types/server.d.ts +0 -110
@@ -5,21 +5,18 @@
5
5
  __defProp(target, name, { get: all[name], enumerable: true });
6
6
  };
7
7
 
8
- // src/account.js
8
+ // src/account.ts
9
9
  var account_exports = {};
10
10
  __export(account_exports, {
11
11
  addresses: () => addresses,
12
12
  customer: () => customer,
13
13
  deleteAddress: () => deleteAddress,
14
- getToken: () => getToken,
15
- handleCallback: () => handleCallback,
16
14
  isLoggedIn: () => isLoggedIn,
17
15
  login: () => login,
18
16
  logout: () => logout,
19
17
  mountAccount: () => mountAccount,
20
18
  order: () => order,
21
19
  orders: () => orders,
22
- query: () => query,
23
20
  render: () => render,
24
21
  requestReturn: () => requestReturn,
25
22
  saveAddress: () => saveAddress,
@@ -28,432 +25,516 @@
28
25
  updateProfile: () => updateProfile
29
26
  });
30
27
 
31
- // tether-core:config.js
32
- var m = window.Tether.__core["config.js"];
33
- var { DEFAULTS, cfg, configure, isConfigured, log, reportError, warn } = m;
28
+ // tether-core:config.ts
29
+ var m = window.Tether.__core["config.ts"];
30
+ var [cfg, isConfigured, log, MARKET_SOURCES, parseMarkets, reportError, warn] = m;
34
31
 
35
- // tether-core:client.js
36
- var m2 = window.Tether.__core["client.js"];
37
- var { TetherError, endpoint, gql, request, unwrap, withContext } = m2;
32
+ // tether-core:client.ts
33
+ var m2 = window.Tether.__core["client.ts"];
34
+ var [endpoint, gql, orNull, shaped, TetherError] = m2;
38
35
 
39
- // tether-core:dom.js
40
- var m3 = window.Tether.__core["dom.js"];
41
- var { attr, busy, cdnUrl, dataKey, emit, formatDate, formatMoney, formatPercent, get, has, hasTemplate, imageUrl, notifyLayoutChanged, parseJSONAttr, productUrl, qsa, readText, repeat, repeatKeyed, sameImage, scoped, sel, setImage, setSelected, setText, setTextTracked, show, toAttributes, watchImages } = m3;
36
+ // tether-core:dom.ts
37
+ var m3 = window.Tether.__core["dom.ts"];
38
+ var [ANALYTICS_EVENTS, attr, attributesFromJson, emit, formatDate, formatMoney, get, has, hasTemplate, isOwnEvent, parseJSONAttr, productUrl, qsa, repeat, sel, setImage, setText, show] = m3;
42
39
 
43
- // tether-core:cart.js
44
- var m4 = window.Tether.__core["cart.js"];
45
- var { Cart, focusables, toLineInput } = m4;
40
+ // tether-core:cart.ts
41
+ var m4 = window.Tether.__core["cart.ts"];
42
+ var [Cart] = m4;
46
43
 
47
- // tether-core:lazy.js
48
- var m5 = window.Tether.__core["lazy.js"];
49
- var { coreModules, loadPart } = m5;
44
+ // tether-core:lazy.ts
45
+ var m5 = window.Tether.__core["lazy.ts"];
46
+ var [loadPart] = m5;
50
47
 
51
- // src/account.js
52
- var KEY = () => `${cfg.prefix}:account:${cfg.domain}`;
53
- var PENDING = () => `${cfg.prefix}:account-login:${cfg.domain}`;
54
- var store = () => cfg.account?.remember ? window.localStorage : window.sessionStorage;
55
- var b64url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
56
- var random = () => b64url(crypto.getRandomValues(new Uint8Array(32)));
57
- var sha256 = async (s) => b64url(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(s)));
58
- var go = (url) => (cfg.account?.navigate || ((u) => window.location.assign(u)))(url);
59
- var read = (s, k) => {
60
- try {
61
- return JSON.parse(s.getItem(k) || "null");
62
- } catch {
63
- return null;
64
- }
65
- };
66
- var write = (s, k, v) => {
48
+ // server/src/shared/json.ts
49
+ function isRecord(value) {
50
+ return value !== null && typeof value === "object" && !Array.isArray(value);
51
+ }
52
+ function field(value, key) {
53
+ return isRecord(value) ? value[key] : void 0;
54
+ }
55
+ function asList(value) {
56
+ return Array.isArray(value) ? value : [];
57
+ }
58
+ function textOrNull(value) {
59
+ return typeof value === "string" && value !== "" ? value : null;
60
+ }
61
+ var numberOrNull = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
62
+ var countOrZero = (value) => numberOrNull(value) ?? 0;
63
+ var listOf = (value, read) => asList(value).map((item) => read(item));
64
+ var nodesOf = (connection, read) => ({
65
+ nodes: listOf(field(connection, "nodes"), read)
66
+ });
67
+ var textList = (value) => listOf(value, textOrNull).filter((line) => line !== null);
68
+
69
+ // server/src/shared/error-message.ts
70
+ function messageOf(error) {
71
+ return error instanceof Error ? error.message : String(error);
72
+ }
73
+
74
+ // src/storage.ts
75
+ function setStored(key, value, area = "localStorage") {
67
76
  try {
68
- if (v) s.setItem(k, JSON.stringify(v));
69
- else s.removeItem(k);
77
+ window[area].setItem(key, value);
70
78
  } catch {
71
79
  }
72
- };
73
- var discovery = null;
74
- var session = null;
75
- var refreshing = null;
76
- var customerCache = null;
77
- var server = () => cfg.account?.server ? new URL(String(cfg.account.server), window.location.href).href.replace(/\/$/, "") : "";
78
- var HINT = () => `${cfg.prefix}:account-hint:${cfg.domain}`;
79
- var serverState = null;
80
- async function serverFetch(path, { method = "GET", body } = {}) {
81
- const res = await fetch(`${server()}${path}`, {
82
- method,
83
- credentials: "include",
84
- headers: method === "POST" ? { "Content-Type": "application/json", "X-Tether-Account": "1" } : {},
85
- body: body ? JSON.stringify(body) : void 0
86
- });
87
- const data = await res.json().catch(() => ({}));
88
- if (!res.ok && res.status !== 401) throw new TetherError(`Customer accounts: ${data.error || res.status}`, { status: res.status, userErrors: data.userErrors });
89
- return data;
90
80
  }
91
- async function serverMe() {
92
- serverState || (serverState = await serverFetch("/me"));
81
+ function removeStored(key, area = "localStorage") {
93
82
  try {
94
- if (serverState.loggedIn) window.localStorage.setItem(HINT(), "1");
95
- else window.localStorage.removeItem(HINT());
83
+ window[area].removeItem(key);
96
84
  } catch {
97
85
  }
98
- return serverState;
99
- }
100
- async function discover() {
101
- if (discovery) return discovery;
102
- const shop = cfg.account?.shopDomain || cfg.domain;
103
- const get2 = (p) => fetch(`https://${shop}/.well-known/${p}`).then((r) => {
104
- if (!r.ok) throw new TetherError(`Customer accounts: ${p} answered ${r.status}`, { status: r.status });
105
- return r.json();
106
- });
107
- const [oidc, api] = await Promise.all([get2("openid-configuration"), get2("customer-account-api")]);
108
- discovery = { ...oidc, graphql: api.graphql_api };
109
- return discovery;
110
- }
111
- var callbackUrl = () => cfg.account?.callbackUrl || `${window.location.origin}${window.location.pathname}`;
112
- function isLoggedIn() {
113
- return server() ? Boolean(serverState?.loggedIn) : Boolean(session?.refresh || session?.access && session.expiresAt > Date.now());
114
- }
115
- async function login({ returnTo = window.location.href, locale } = {}) {
116
- if (server()) {
117
- const u2 = new URL(`${server()}/login`);
118
- u2.searchParams.set("returnTo", returnTo);
119
- const lang2 = locale || cfg.account.locale || (cfg.locale || "").split("-")[0];
120
- if (lang2) u2.searchParams.set("locale", lang2);
121
- if (cfg.country) u2.searchParams.set("country", String(cfg.country).toUpperCase());
122
- go(u2.toString());
123
- return;
86
+ }
87
+
88
+ // src/account-marks.ts
89
+ var hintKey = () => `${cfg.prefix}:account-hint:${cfg.domain}`;
90
+ var isLoginOutcome = (value) => value === "in" || value === "expired" || value === "error";
91
+
92
+ // server/src/shared/account-orders.ts
93
+ function moneyOrNull(value) {
94
+ const amount = textOrNull(field(value, "amount"));
95
+ const currencyCode = textOrNull(field(value, "currencyCode"));
96
+ return amount === null || currencyCode === null ? null : { amount, currencyCode };
97
+ }
98
+ function imageOrNull(value) {
99
+ const url = textOrNull(field(value, "url"));
100
+ return url === null ? null : { url, altText: textOrNull(field(value, "altText")) };
101
+ }
102
+ var toOrderLine = (value) => ({
103
+ title: textOrNull(field(value, "title")),
104
+ variantTitle: textOrNull(field(value, "variantTitle")),
105
+ quantity: countOrZero(field(value, "quantity")),
106
+ image: imageOrNull(field(value, "image"))
107
+ });
108
+ var toAccountOrder = (value, id) => ({
109
+ id,
110
+ name: textOrNull(field(value, "name")),
111
+ processedAt: textOrNull(field(value, "processedAt")),
112
+ financialStatus: textOrNull(field(value, "financialStatus")),
113
+ fulfillmentStatus: textOrNull(field(value, "fulfillmentStatus")),
114
+ statusPageUrl: textOrNull(field(value, "statusPageUrl")),
115
+ totalPrice: moneyOrNull(field(value, "totalPrice")),
116
+ lineItems: nodesOf(field(value, "lineItems"), toOrderLine)
117
+ });
118
+ function readOrders(value) {
119
+ const orders2 = [];
120
+ for (const item of asList(value)) {
121
+ const id = textOrNull(field(item, "id"));
122
+ if (id === null) return { kind: "missing-id" };
123
+ orders2.push(toAccountOrder(item, id));
124
124
  }
125
- if (!cfg.account?.clientId) throw new TetherError("Customer accounts: set account.clientId", { code: "NOT_CONFIGURED" });
126
- const d = await discover();
127
- const state = random();
128
- const nonce = random();
129
- const verifier = random();
130
- const redirect = callbackUrl();
131
- write(window.sessionStorage, PENDING(), { state, nonce, verifier, redirect, returnTo, at: Date.now() });
132
- const u = new URL(d.authorization_endpoint);
133
- const p = {
134
- client_id: cfg.account.clientId,
135
- scope: "openid email customer-account-api:full",
136
- response_type: "code",
137
- redirect_uri: redirect,
138
- state,
139
- nonce,
140
- code_challenge: await sha256(verifier),
141
- code_challenge_method: "S256"
125
+ return { kind: "read", orders: orders2 };
126
+ }
127
+
128
+ // src/account/answers.ts
129
+ var unexpected = (what) => new TetherError(`Customer accounts: the answer has no ${what}`);
130
+ function toAccountCustomer(value) {
131
+ const customer2 = isRecord(value) ? value : {};
132
+ const id = textOrNull(customer2["id"]);
133
+ const email = textOrNull(customer2["email"]);
134
+ const address = customer2["address"];
135
+ return {
136
+ ...id === null ? {} : { id },
137
+ firstName: textOrNull(customer2["firstName"]),
138
+ lastName: textOrNull(customer2["lastName"]),
139
+ displayName: textOrNull(customer2["displayName"]),
140
+ emailAddress: email === null ? null : { emailAddress: email },
141
+ defaultAddress: Array.isArray(address) ? { formatted: textList(address) } : null
142
142
  };
143
- const lang = locale || cfg.account.locale || (cfg.locale || "").split("-")[0];
144
- if (lang) p.locale = lang;
145
- if (cfg.country) p.region_country = String(cfg.country).toUpperCase();
146
- Object.entries(p).forEach(([k, v]) => u.searchParams.set(k, v));
147
- go(u.toString());
148
- }
149
- async function tokenRequest(params) {
150
- const d = await discover();
151
- const res = await fetch(cfg.account.tokenUrl || d.token_endpoint, { method: "POST", body: new URLSearchParams(params) });
152
- const data = await res.json().catch(() => ({}));
153
- if (!res.ok || !data.access_token) throw new TetherError(`Customer accounts: token ${res.status} ${data.error || ""}`.trim(), { status: res.status, code: res.status === 400 || res.status === 401 ? "SESSION_ENDED" : "TOKEN_FAILED" });
154
- return data;
155
- }
156
- function decodeJwt(t) {
157
- try {
158
- return JSON.parse(atob(t.split(".")[1].replace(/-/g, "+").replace(/_/g, "/")));
159
- } catch {
160
- return null;
161
- }
162
143
  }
163
- function keep(data, previous = session) {
164
- session = {
165
- access: data.access_token,
166
- expiresAt: Date.now() + Math.max(0, (Number(data.expires_in) || 3600) - 120) * 1e3,
167
- refresh: data.refresh_token || previous?.refresh,
168
- idToken: data.id_token || previous?.idToken
144
+ function toAccountOrders(value) {
145
+ const reading = readOrders(value);
146
+ if (reading.kind === "missing-id") throw unexpected("order id");
147
+ return reading.orders;
148
+ }
149
+ function toUserError(value) {
150
+ const message = textOrNull(field(value, "message"));
151
+ if (message === null) return null;
152
+ const fieldPath = field(value, "field");
153
+ return {
154
+ message,
155
+ field: Array.isArray(fieldPath) ? textList(fieldPath) : null,
156
+ code: textOrNull(field(value, "code"))
169
157
  };
170
- write(store(), KEY(), session);
171
- }
172
- async function handleCallback() {
173
- const q = new URLSearchParams(window.location.search);
174
- const pending = read(window.sessionStorage, PENDING());
175
- if (!pending || !q.has("code") && !q.has("error") || q.get("state") !== pending.state) return false;
176
- write(window.sessionStorage, PENDING(), null);
177
- const clean = new URL(window.location.href);
178
- ["code", "state", "iss", "error", "error_description"].forEach((k) => clean.searchParams.delete(k));
179
- window.history.replaceState(window.history.state, "", clean);
180
- if (q.has("error")) {
181
- emit("account-error", { error: q.get("error") });
182
- return false;
183
- }
184
- if (Date.now() - pending.at > 10 * 6e4) throw new TetherError("Customer accounts: the login took too long, please try again");
185
- const data = await tokenRequest({
186
- grant_type: "authorization_code",
187
- client_id: cfg.account.clientId,
188
- redirect_uri: pending.redirect,
189
- code: q.get("code"),
190
- code_verifier: pending.verifier
191
- });
192
- const d = await discover();
193
- const claims = decodeJwt(data.id_token || "");
194
- const aud = [].concat(claims?.aud || []);
195
- if (!claims || claims.nonce !== pending.nonce || claims.iss !== d.issuer || !aud.includes(cfg.account.clientId) || claims.exp * 1e3 < Date.now()) {
196
- throw new TetherError("Customer accounts: the login answer did not check out (nonce/issuer/audience)");
158
+ }
159
+ var toUserErrors = (value) => listOf(value, toUserError).filter((error) => error !== null);
160
+
161
+ // src/account/server-client.ts
162
+ var known = null;
163
+ function serverBase() {
164
+ const server = cfg.account?.server;
165
+ if (!server) {
166
+ throw new TetherError("Customer accounts need the Worker: set account.server", { code: "NOT_CONFIGURED" });
197
167
  }
198
- keep(data, null);
199
- await linkCart();
200
- emit("account-login", {});
201
- const back = pending.returnTo && new URL(pending.returnTo, window.location.href);
202
- if (back && back.origin === window.location.origin && back.href !== clean.href) go(back.href);
203
- return true;
204
- }
205
- async function getToken() {
206
- if (server()) return null;
207
- if (!session) session = read(store(), KEY());
208
- if (!session) return null;
209
- if (session.access && session.expiresAt > Date.now()) return session.access;
210
- if (!session.refresh) {
211
- endSession();
212
- return null;
168
+ return new URL(server, window.location.href).href.replace(/\/$/, "");
169
+ }
170
+ function setLoginHint(loggedIn) {
171
+ if (loggedIn) setStored(hintKey(), "1");
172
+ else removeStored(hintKey());
173
+ }
174
+ async function fetchFromWorker(url, init) {
175
+ try {
176
+ return await fetch(url, init);
177
+ } catch (error) {
178
+ throw new TetherError(`Network error: ${messageOf(error)}`, {
179
+ code: "NETWORK"
180
+ });
213
181
  }
214
- refreshing || (refreshing = (async () => {
215
- try {
216
- keep(await tokenRequest({ grant_type: "refresh_token", client_id: cfg.account.clientId, refresh_token: session.refresh }));
217
- await linkCart();
218
- } catch (err) {
219
- if (err.code === "SESSION_ENDED") endSession();
220
- else throw err;
221
- } finally {
222
- refreshing = null;
223
- }
224
- })());
225
- await refreshing;
226
- return session?.access || null;
227
- }
228
- function endSession() {
229
- session = null;
230
- customerCache = null;
231
- write(store(), KEY(), null);
232
- Cart.customerAccessToken = null;
233
- }
234
- async function linkCart() {
235
- Cart.customerAccessToken = session?.access || null;
236
- if (Cart.id() && session?.access) {
237
- await Cart.setBuyerIdentity({ customerAccessToken: session.access }, { create: false }).catch((err) => reportError(err, "account cart"));
182
+ }
183
+ async function readJsonObjectOrEmpty(response) {
184
+ let json;
185
+ try {
186
+ json = await response.json();
187
+ } catch {
188
+ return {};
238
189
  }
190
+ return isRecord(json) ? json : {};
239
191
  }
240
- async function query(q, variables = {}) {
241
- if (server()) throw new TetherError("Customer accounts in server mode only answer customer() and orders()", { code: "NOT_ALLOWED" });
242
- const token = await getToken();
243
- if (!token) throw new TetherError("Not logged in", { code: "NOT_LOGGED_IN" });
244
- const d = await discover();
245
- const run = (t) => fetch(d.graphql, { method: "POST", headers: { "Content-Type": "application/json", Authorization: t }, body: JSON.stringify({ query: q, variables }) });
246
- let res = await run(token);
247
- if (res.status === 401 && session?.refresh) {
248
- session.expiresAt = 0;
249
- res = await run(await getToken());
192
+ async function serverFetch(path, { method = "GET", body } = {}) {
193
+ const init = { method, credentials: "include" };
194
+ if (method === "POST") init.headers = { "Content-Type": "application/json", "X-Tether-Account": "1" };
195
+ if (body) init.body = JSON.stringify(body);
196
+ const response = await fetchFromWorker(`${serverBase()}${path}`, init);
197
+ if (response.status === 401) return { kind: "logged-out" };
198
+ const data = await readJsonObjectOrEmpty(response);
199
+ if (!response.ok) {
200
+ const reason = textOrNull(data["error"]) ?? String(response.status);
201
+ const userErrors = toUserErrors(data["userErrors"]);
202
+ throw new TetherError(`Customer accounts: ${reason}`, {
203
+ status: response.status,
204
+ ...userErrors.length > 0 && { userErrors },
205
+ // The Worker marks refused input (a 400, or a 404 for a contract that isn't the customer's) with `code`.
206
+ ...data["code"] === "INVALID" && { code: "INVALID" }
207
+ });
250
208
  }
251
- const json = await res.json().catch(() => ({}));
252
- if (!res.ok || json.errors?.length) throw new TetherError(json.errors?.map((e) => e.message).join(" | ") || `Customer accounts: ${res.status}`, { status: res.status });
253
- return json.data;
209
+ return { kind: "answer", data };
254
210
  }
255
- async function customer() {
256
- if (server()) {
257
- const me = await serverMe();
258
- return me.loggedIn ? { ...me.customer, emailAddress: { emailAddress: me.customer.email }, defaultAddress: { formatted: me.customer.address } } : null;
211
+ async function serverLogin() {
212
+ if (!known) {
213
+ const answer = await serverFetch("/me");
214
+ known = answer.kind === "answer" && answer.data["loggedIn"] === true ? { loggedIn: true, customer: toAccountCustomer(answer.data["customer"]) } : { loggedIn: false };
259
215
  }
260
- if (customerCache) return customerCache;
261
- const data = await query(`query TetherCustomer{customer{id firstName lastName displayName emailAddress{emailAddress}defaultAddress{formatted}}}`);
262
- customerCache = data.customer;
263
- return customerCache;
264
- }
265
- async function orders({ first = 10 } = {}) {
266
- if (server()) return (await serverFetch(`/orders?first=${first}`)).orders || [];
267
- const data = await query(`query TetherOrders($first:Int!){customer{orders(first:$first,sortKey:PROCESSED_AT,reverse:true){nodes{id name processedAt financialStatus fulfillmentStatus statusPageUrl totalPrice{amount currencyCode}lineItems(first:20){nodes{title variantTitle quantity image{url altText}}}}}}}`, { first });
268
- return data.customer?.orders?.nodes || [];
269
- }
270
- async function logout({ returnTo = cfg.account?.logoutUrl || `${window.location.origin}/` } = {}) {
271
- if (server()) {
272
- const r = await serverFetch("/logout", { method: "POST", body: { returnTo, cartId: Cart.id() } });
273
- serverState = { loggedIn: false };
274
- try {
275
- window.localStorage.removeItem(HINT());
276
- } catch {
216
+ setLoginHint(known.loggedIn);
217
+ return known;
218
+ }
219
+ var isServerLoggedIn = () => known?.loggedIn === true;
220
+ function forgetServerLogin() {
221
+ known = null;
222
+ }
223
+ function navigate(url) {
224
+ const replacement = cfg.account?.navigate;
225
+ if (replacement) replacement(url);
226
+ else window.location.assign(url);
227
+ }
228
+ var loginLanguage = (locale) => locale || cfg.account?.locale || (cfg.locale || "").split("-")[0] || "";
229
+ function serverLoginRedirect({ returnTo, locale }) {
230
+ const url = new URL(`${serverBase()}/login`);
231
+ url.searchParams.set("returnTo", returnTo);
232
+ const language = loginLanguage(locale);
233
+ if (language) url.searchParams.set("locale", language);
234
+ if (cfg.country) url.searchParams.set("country", cfg.country.toUpperCase());
235
+ navigate(url.toString());
236
+ }
237
+ async function serverCheckoutUrl() {
238
+ const cartId = Cart.id();
239
+ if (!(await serverLogin()).loggedIn || !cartId) return null;
240
+ const answer = await serverFetch("/cart", { method: "POST", body: { cartId } });
241
+ if (answer.kind === "logged-out") return null;
242
+ return textOrNull(answer.data["checkoutUrl"]);
243
+ }
244
+ async function serverLogout(returnTo) {
245
+ const answer = await serverFetch("/logout", { method: "POST", body: { returnTo, cartId: Cart.id() } });
246
+ known = null;
247
+ setLoginHint(false);
248
+ emit("account-logout", {});
249
+ const logoutUrl = answer.kind === "answer" ? textOrNull(answer.data["logoutUrl"]) : null;
250
+ if (!logoutUrl) return "stayed";
251
+ navigate(logoutUrl);
252
+ return "redirected";
253
+ }
254
+
255
+ // tether-core:shopify-values.ts
256
+ var m6 = window.Tether.__core["shopify-values.ts"];
257
+ var [isHttpsUrl] = m6;
258
+
259
+ // src/account/markup.ts
260
+ function showIn(root, name, conditions) {
261
+ qsa(root, sel(name)).forEach((el) => {
262
+ const key = get(el, name) ?? "";
263
+ if (Object.hasOwn(conditions, key)) show(el, conditions[key] === true);
264
+ });
265
+ }
266
+ var bindsIn = (row, name) => qsa(row, sel(name)).concat(row.matches(sel(name)) ? [row] : []);
267
+ var ownValue = (values, key) => Object.hasOwn(values, key) ? values[key] : void 0;
268
+
269
+ // src/account/order-lines.ts
270
+ var lineTexts = (line, returnable) => ({
271
+ title: line.title || line.name || "",
272
+ "variant-title": line.variantTitle || "",
273
+ quantity: String(line.quantity),
274
+ price: formatMoney(line.price),
275
+ total: formatMoney(line.totalPrice),
276
+ sku: line.sku || "",
277
+ returnable: returnable === null ? void 0 : String(returnable)
278
+ });
279
+ var isInput = (el) => el.tagName === "INPUT";
280
+ function fillReturnControls(row, line, returnable) {
281
+ showIn(row, "order-line-show", { returnable: returnable > 0 });
282
+ qsa(row, sel("return-quantity")).forEach((input) => {
283
+ if (!isInput(input)) return;
284
+ input.max = String(returnable);
285
+ input.min = "0";
286
+ if (!input.value) input.value = "0";
287
+ input.disabled = returnable < 1;
288
+ });
289
+ qsa(row, `select${sel("return-reason")}`).forEach((select) => {
290
+ if (select.dataset["tetherFilled"] === line.id) return;
291
+ select.dataset["tetherFilled"] = line.id;
292
+ (line.suggestedReturnReasonDefinitions?.nodes ?? []).forEach((reason) => {
293
+ select.appendChild(new Option(reason.name ?? "", reason.id));
294
+ });
295
+ });
296
+ }
297
+ function fillLines(box, lines, returns) {
298
+ repeat(box, lines, (row, line) => {
299
+ const id = line.id;
300
+ const returnable = returns && id ? returns.get(id) ?? 0 : null;
301
+ if (id) row.dataset["tetherLine"] = id;
302
+ const texts = lineTexts(line, returnable);
303
+ bindsIn(row, "order-line").forEach((el) => {
304
+ const key = get(el, "order-line") ?? "";
305
+ if (key === "image") {
306
+ if (line.image) setImage(el, line.image.url, line.image.altText || line.title || "");
307
+ return;
308
+ }
309
+ const text = ownValue(texts, key);
310
+ if (text !== void 0) setText(el, text);
311
+ });
312
+ if (id && returnable !== null) fillReturnControls(row, { ...line, id }, returnable);
313
+ });
314
+ }
315
+
316
+ // src/account/order-list.ts
317
+ var DEFAULT_ORDER_COUNT = 10;
318
+ var MAX_ORDER_COUNT = 50;
319
+ var isOrderCount = (value) => typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= MAX_ORDER_COUNT;
320
+ function listLimit(limit) {
321
+ const asked = Number.parseInt(limit || String(DEFAULT_ORDER_COUNT), 10) || DEFAULT_ORDER_COUNT;
322
+ return Math.min(Math.max(asked, 1), MAX_ORDER_COUNT);
323
+ }
324
+ var numericId = (gid) => gid.replace(/\?.*$/, "").replace(/^.*\//, "");
325
+ function orderUrl(order2) {
326
+ const template = cfg.account?.orderUrl;
327
+ if (template) return template.replaceAll("{id}", encodeURIComponent(numericId(order2.id)));
328
+ const statusPage = order2.statusPageUrl;
329
+ return statusPage !== null && isHttpsUrl(statusPage) ? statusPage : null;
330
+ }
331
+ var orderTexts = (order2) => ({
332
+ name: order2.name ?? "",
333
+ date: formatDate(order2.processedAt),
334
+ total: formatMoney(order2.totalPrice),
335
+ status: order2.financialStatus?.toLowerCase(),
336
+ fulfillment: order2.fulfillmentStatus?.toLowerCase(),
337
+ items: order2.lineItems.nodes.map((line) => `${String(line.quantity)}\xD7 ${line.title ?? ""}`).join(", ")
338
+ });
339
+ function fillOrder(node, order2) {
340
+ const lines = order2.lineItems.nodes;
341
+ const url = orderUrl(order2);
342
+ const texts = orderTexts(order2);
343
+ qsa(node, sel("order-bind")).forEach((el) => {
344
+ const key = get(el, "order-bind") ?? "";
345
+ if (key === "url") {
346
+ if (url) el.setAttribute("href", url);
347
+ return;
277
348
  }
278
- emit("account-logout", {});
279
- if (r.logoutUrl) go(r.logoutUrl);
280
- else render(document);
281
- return;
282
- }
283
- const idToken = session?.idToken || read(store(), KEY())?.idToken;
284
- if (Cart.id()) await Cart.setBuyerIdentity({ customerAccessToken: null }, { create: false }).catch(() => {
349
+ if (key === "image") {
350
+ const image = lines.find((line) => line.image)?.image;
351
+ if (image) setImage(el, image.url, image.altText || lines[0]?.title || "");
352
+ return;
353
+ }
354
+ const text = ownValue(texts, key);
355
+ if (text !== void 0) setText(el, text);
285
356
  });
286
- endSession();
287
- emit("account-logout", {});
288
- if (!idToken) {
289
- render(document);
290
- return;
291
- }
292
- const d = await discover();
293
- const u = new URL(d.end_session_endpoint);
294
- u.searchParams.set("id_token_hint", idToken);
295
- u.searchParams.set("post_logout_redirect_uri", returnTo);
296
- go(u.toString());
357
+ qsa(node, sel("order-lines")).forEach((box) => {
358
+ fillLines(box, lines, null);
359
+ });
360
+ if (get(node, "order-bind") === "url" && url) node.setAttribute("href", url);
297
361
  }
298
- var https = (u) => {
299
- try {
300
- return new URL(u).protocol === "https:";
301
- } catch {
302
- return false;
303
- }
304
- };
305
- var date = (s) => {
362
+ async function renderOrderList(root, loadOrders) {
363
+ const lists = qsa(root, sel("orders"));
364
+ const first = lists[0];
365
+ if (!first) return;
366
+ showIn(root, "show", { "orders-loading": true, "orders-empty": false });
367
+ const limit = listLimit(get(first, "limit"));
368
+ let orders2;
306
369
  try {
307
- return new Date(s).toLocaleDateString(cfg.locale || void 0);
308
- } catch {
309
- return s;
370
+ orders2 = await loadOrders(limit);
371
+ } catch (error) {
372
+ reportError(error, "orders");
373
+ orders2 = null;
310
374
  }
311
- };
312
- async function render(root = document) {
313
- const logged = server() ? Boolean((await serverMe().catch(() => ({ loggedIn: false }))).loggedIn) : isLoggedIn() || Boolean(await getToken().catch(() => null));
314
- qsa(root, sel("show")).forEach((el) => {
315
- const k = get(el, "show");
316
- if (k === "logged-in") show(el, logged);
317
- if (k === "logged-out") show(el, !logged);
375
+ showIn(root, "show", { "orders-loading": false, "orders-empty": orders2?.length === 0 });
376
+ lists.forEach((list) => {
377
+ repeat(list, orders2 ?? [], fillOrder);
318
378
  });
319
- qsa(root, sel("account-link")).forEach((a) => {
320
- const k = get(a, "account-link");
321
- const u = k === "payment" && (cfg.account?.paymentUrl || cfg.account?.subscriptionsUrl) || k === "subscriptions" && cfg.account?.subscriptionsUrl;
322
- a.setAttribute("href", u || `https://${cfg.domain}/account`);
379
+ }
380
+
381
+ // src/account.ts
382
+ var PAGE_ATTRIBUTES = ["order", "addresses", "subscriptions", "profile-form", "address-form"];
383
+ var isLoggedIn = isServerLoggedIn;
384
+ function login({
385
+ returnTo = window.location.href,
386
+ locale
387
+ } = {}) {
388
+ return new Promise((resolve) => {
389
+ serverLoginRedirect({ returnTo, locale });
390
+ resolve();
323
391
  });
324
- if (!logged) return;
325
- await bindCustomer(root);
326
- await Promise.all([renderOrderList(root), PAGE_ATTRS.some((n) => qsa(root, sel(n)).length) ? pages().then((m6) => m6.renderPages(root)) : null]);
327
392
  }
328
- async function renderOrderList(root) {
329
- const lists = qsa(root, sel("orders"));
330
- if (lists.length) {
331
- const ordersShow = (loading, empty) => qsa(root, sel("show")).forEach((el) => {
332
- const k = get(el, "show");
333
- if (k === "orders-loading") show(el, loading);
334
- if (k === "orders-empty") show(el, empty);
335
- });
336
- ordersShow(true, false);
337
- let failed = false;
338
- const list = await orders({ first: parseInt(get(lists[0], "limit") || "10", 10) }).catch((err) => {
339
- reportError(err, "orders");
340
- failed = true;
341
- return [];
393
+ async function customer() {
394
+ const me = await serverLogin();
395
+ return me.loggedIn ? me.customer : null;
396
+ }
397
+ async function orders({ first = DEFAULT_ORDER_COUNT } = {}) {
398
+ if (!isOrderCount(first)) {
399
+ throw new TetherError(`Customer accounts: orders() takes first from 1 to ${String(MAX_ORDER_COUNT)}`, {
400
+ code: "INVALID"
342
401
  });
343
- ordersShow(false, !failed && list.length === 0);
344
- lists.forEach((c) => repeat(c, list, (node, o) => {
345
- const items = o.lineItems?.nodes || [];
346
- qsa(node, sel("order-bind")).forEach((el) => {
347
- const k = get(el, "order-bind");
348
- if (k === "url") {
349
- const u = orderUrl(o);
350
- if (u) el.setAttribute("href", u);
351
- return;
352
- }
353
- if (k === "image") {
354
- const im = items.find((l) => l.image)?.image;
355
- if (im) setImage(el, im.url, im.altText || items[0]?.title || "");
356
- return;
357
- }
358
- const v = { name: o.name, date: date(o.processedAt), total: formatMoney(o.totalPrice), status: o.financialStatus?.toLowerCase(), fulfillment: o.fulfillmentStatus?.toLowerCase(), items: items.map((l) => `${l.quantity}\xD7 ${l.title}`).join(", ") }[k];
359
- if (v != null) setText(el, v);
360
- });
361
- qsa(node, sel("order-lines")).forEach((box) => fillLines(box, items));
362
- if (get(node, "order-bind") === "url" && orderUrl(o)) node.setAttribute("href", orderUrl(o));
363
- }));
364
402
  }
403
+ const answer = await serverFetch(`/orders?${new URLSearchParams({ first: String(first) }).toString()}`);
404
+ return answer.kind === "logged-out" ? [] : toAccountOrders(answer.data["orders"]);
365
405
  }
366
- var nid = (g) => String(g || "").split("/").pop().split("?")[0];
367
- function orderUrl(o) {
368
- const t = cfg.account?.orderUrl;
369
- if (t) return String(t).replace("{id}", encodeURIComponent(nid(o.id)));
370
- return https(o.statusPageUrl) ? o.statusPageUrl : null;
371
- }
372
- function fillLines(box, items) {
373
- repeat(box, items, (row, l) => {
374
- qsa(row, sel("order-line")).concat(row.matches(sel("order-line")) ? [row] : []).forEach((el) => {
375
- const k = get(el, "order-line");
376
- if (k === "image") {
377
- if (l.image) setImage(el, l.image.url, l.image.altText || l.title);
378
- return;
379
- }
380
- const v = { title: l.title, "variant-title": l.variantTitle || "", quantity: String(l.quantity) }[k];
381
- if (v != null) setText(el, v);
382
- });
406
+ async function logout({
407
+ returnTo = cfg.account?.logoutUrl || `${window.location.origin}/`
408
+ } = {}) {
409
+ if (await serverLogout(returnTo) === "stayed") await render(document);
410
+ }
411
+ async function loginState() {
412
+ try {
413
+ return (await serverLogin()).loggedIn ? { kind: "logged-in" } : { kind: "logged-out" };
414
+ } catch (error) {
415
+ return { kind: "unknown", error };
416
+ }
417
+ }
418
+ var usesPages = (root) => PAGE_ATTRIBUTES.some((name) => qsa(root, sel(name)).length > 0);
419
+ function linkShopifyPages(root) {
420
+ qsa(root, sel("account-link")).forEach((link) => {
421
+ link.setAttribute("href", shopifyPageUrl(get(link, "account-link")));
383
422
  });
384
423
  }
385
- var PAGE_ATTRS = ["order", "addresses", "subscriptions", "profile-form", "address-form"];
386
- var pagesPart = null;
424
+ function shopifyPageUrl(page) {
425
+ const account = cfg.account;
426
+ const fallback = `https://${cfg.domain}/account`;
427
+ if (page === "payment") return account?.paymentUrl || account?.subscriptionsUrl || fallback;
428
+ if (page === "subscriptions") return account?.subscriptionsUrl || fallback;
429
+ return fallback;
430
+ }
431
+ async function render(root = document) {
432
+ const state = await loginState();
433
+ if (state.kind === "unknown") reportError(state.error, "account");
434
+ const loggedIn = state.kind === "logged-in";
435
+ qsa(root, sel("show")).forEach((el) => {
436
+ const condition = get(el, "show");
437
+ if (condition === "logged-in") show(el, loggedIn);
438
+ if (condition === "logged-out") show(el, !loggedIn);
439
+ });
440
+ linkShopifyPages(root);
441
+ if (state.kind === "unknown" && usesPages(root)) {
442
+ (await pages()).showUnavailable(root);
443
+ return;
444
+ }
445
+ if (!loggedIn) return;
446
+ await bindCustomer(root);
447
+ await Promise.all([
448
+ renderOrderList(root, (first) => orders({ first })),
449
+ usesPages(root) ? pages().then((part) => part.renderPages(root)) : null
450
+ ]);
451
+ }
387
452
  async function bindCustomer(root) {
388
453
  const binds = qsa(root, sel("account-bind"));
389
454
  if (!binds.length) return;
390
- const c = await customer().catch((err) => {
391
- reportError(err, "account");
392
- return null;
393
- });
455
+ let profile;
456
+ try {
457
+ profile = await customer();
458
+ } catch (error) {
459
+ reportError(error, "account");
460
+ return;
461
+ }
462
+ if (!profile) return;
463
+ const texts = {
464
+ "first-name": profile.firstName,
465
+ "last-name": profile.lastName,
466
+ name: profile.displayName,
467
+ email: profile.emailAddress?.emailAddress,
468
+ address: profile.defaultAddress?.formatted.join(", ")
469
+ };
394
470
  binds.forEach((el) => {
395
- const v = { "first-name": c?.firstName, "last-name": c?.lastName, name: c?.displayName, email: c?.emailAddress?.emailAddress, address: c?.defaultAddress?.formatted?.join(", ") }[get(el, "account-bind")];
396
- if (v != null) setText(el, v);
471
+ const text = ownValue(texts, get(el, "account-bind") ?? "");
472
+ if (text != null) setText(el, text);
397
473
  });
398
474
  }
475
+ var pagesPart = null;
399
476
  function pages() {
400
- pagesPart || (pagesPart = loadPart("account-pages").then((m6) => {
401
- m6.setup({ server, serverFetch, query, customer, bindCustomer, reset: () => {
402
- customerCache = null;
403
- serverState = null;
404
- } });
405
- return m6;
477
+ pagesPart ?? (pagesPart = loadPart("account-pages").then((part) => {
478
+ part.setup({ serverFetch, customer, bindCustomer, reset: forgetServerLogin });
479
+ return part;
406
480
  }));
407
481
  return pagesPart;
408
482
  }
409
- var viaPages = (name) => (...a) => pages().then((m6) => m6[name](...a));
410
- var order = viaPages("order");
411
- var updateProfile = viaPages("updateProfile");
412
- var addresses = viaPages("addresses");
413
- var saveAddress = viaPages("saveAddress");
414
- var deleteAddress = viaPages("deleteAddress");
415
- var subscriptions = viaPages("subscriptions");
416
- var subscriptionAction = viaPages("subscriptionAction");
417
- var requestReturn = viaPages("requestReturn");
418
- var bound = false;
419
- async function mountAccount(root = document) {
420
- if (!bound) {
421
- bound = true;
422
- document.addEventListener("click", (e) => {
423
- const t = e.target;
424
- if (t.closest?.(sel("account-login"))) {
425
- e.preventDefault();
426
- login().catch((err) => reportError(err, "login"));
427
- } else if (t.closest?.(sel("account-logout"))) {
428
- e.preventDefault();
429
- logout().catch((err) => reportError(err, "logout"));
430
- }
483
+ var order = (id) => pages().then((part) => part.order(id));
484
+ var updateProfile = (fields) => pages().then((part) => part.updateProfile(fields));
485
+ var addresses = () => pages().then((part) => part.addresses());
486
+ var saveAddress = (address, options) => pages().then((part) => part.saveAddress(address, options));
487
+ var deleteAddress = (id) => pages().then((part) => part.deleteAddress(id));
488
+ var subscriptions = () => pages().then((part) => part.subscriptions());
489
+ var subscriptionAction = (id, action) => pages().then((part) => part.subscriptionAction(id, action));
490
+ var requestReturn = (orderId, lines) => pages().then((part) => part.requestReturn(orderId, lines));
491
+ function onClick(event) {
492
+ const target = event.target;
493
+ if (!(target instanceof window.Element)) return;
494
+ if (target.closest(sel("account-login"))) {
495
+ event.preventDefault();
496
+ login().catch((error) => {
497
+ reportError(error, "login");
431
498
  });
432
- if (server()) {
433
- const u = new URL(window.location.href);
434
- if (u.searchParams.get("account")) {
435
- u.searchParams.delete("account");
436
- window.history.replaceState(window.history.state, "", u);
437
- emit("account-login", {});
438
- }
439
- Cart.beforeCheckout = async () => {
440
- if (!(await serverMe()).loggedIn || !Cart.id()) return null;
441
- const r = await serverFetch("/cart", { method: "POST", body: { cartId: Cart.id() } }).catch(() => null);
442
- return r?.checkoutUrl ? `${r.checkoutUrl}${r.checkoutUrl.includes("?") ? "&" : "?"}logged_in=true` : null;
443
- };
444
- } else {
445
- try {
446
- await handleCallback();
447
- } catch (err) {
448
- reportError(err, "login");
449
- }
450
- if (!Cart.customerAccessToken && await getToken().catch(() => null)) Cart.customerAccessToken = session.access;
451
- }
452
- log("account", isLoggedIn() ? "logged in" : "logged out");
499
+ } else if (target.closest(sel("account-logout"))) {
500
+ event.preventDefault();
501
+ logout().catch((error) => {
502
+ reportError(error, "logout");
503
+ });
504
+ }
505
+ }
506
+ function finishServerLogin() {
507
+ const url = new URL(window.location.href);
508
+ const outcome = url.searchParams.get("account");
509
+ if (!isLoginOutcome(outcome)) return;
510
+ url.searchParams.delete("account");
511
+ window.history.replaceState(window.history.state, "", url);
512
+ switch (outcome) {
513
+ case "in":
514
+ emit("account-login", {});
515
+ break;
516
+ case "expired":
517
+ case "error":
518
+ emit("account-error", { error: outcome });
519
+ break;
520
+ }
521
+ }
522
+ var started = false;
523
+ async function mountAccount(root = document) {
524
+ if (started) {
525
+ await render(root);
526
+ return;
453
527
  }
528
+ started = true;
529
+ document.addEventListener("click", onClick);
530
+ finishServerLogin();
531
+ Cart.setCheckoutResolver(serverCheckoutUrl);
454
532
  await render(root);
533
+ log("account", isLoggedIn() ? "logged in" : "logged out");
455
534
  }
456
535
 
457
- // src/parts/account.js
458
- window.Tether.__parts.account = account_exports;
536
+ // src/parts/account.ts
537
+ var { Tether } = window;
538
+ if (!Tether) throw new Error("tether.account.js runs after the Tether script, which loads it");
539
+ Tether.__parts.account = account_exports;
459
540
  })();