@occluze/tether 0.6.8 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -28
- package/dist/esm/astro.js +2 -2
- package/dist/esm/chunks/account-CXMA5RPZ.js +1 -0
- package/dist/esm/chunks/account-pages-IZO7FEOZ.js +1 -0
- package/dist/esm/chunks/analytics-PG5FPRDN.js +1 -0
- package/dist/esm/chunks/chunk-4HD4J5JC.js +1 -0
- package/dist/esm/chunks/chunk-6TFMLT4A.js +4 -0
- package/dist/esm/chunks/chunk-BBVUVPMO.js +1 -0
- package/dist/esm/chunks/chunk-E5YO6274.js +1 -0
- package/dist/esm/chunks/chunk-EW5SYO6L.js +1 -0
- package/dist/esm/chunks/chunk-HJY2JSNB.js +1 -0
- package/dist/esm/chunks/chunk-MQYB6BH4.js +1 -0
- package/dist/esm/chunks/chunk-N3V45JPS.js +1 -0
- package/dist/esm/chunks/chunk-RPMBCAGS.js +1 -0
- package/dist/esm/chunks/chunk-VFNHIGCS.js +1 -0
- package/dist/esm/chunks/chunk-WT7RGGJU.js +1 -0
- package/dist/esm/chunks/chunk-YN6LEBEN.js +1 -0
- package/dist/esm/chunks/chunk-Z6HS3H7K.js +1 -0
- package/dist/esm/chunks/diagnose-XCEBQKBO.js +14 -0
- package/dist/esm/chunks/extras-4HU3VSBQ.js +1 -0
- package/dist/esm/chunks/markets-ui-M4IQ62CT.js +1 -0
- package/dist/esm/chunks/search-A5DS2IOS.js +1 -0
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +2 -2
- package/dist/presets/barba.esm.js +19 -23
- package/dist/tether.account-pages.js +332 -367
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +253 -492
- package/dist/tether.account.min.js +1 -1
- package/dist/tether.analytics.js +82 -89
- package/dist/tether.analytics.min.js +1 -1
- package/dist/tether.diagnose.js +52 -34
- package/dist/tether.diagnose.min.js +2 -2
- package/dist/tether.extras.js +42 -37
- package/dist/tether.extras.min.js +1 -1
- package/dist/tether.js +1361 -935
- package/dist/tether.markets.js +89 -91
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +0 -0
- package/dist/tether.min.js.map +4 -4
- package/dist/tether.search.js +60 -69
- package/dist/tether.search.min.js +1 -1
- package/dist/types/server/src/shared/account-orders.d.ts +44 -0
- package/dist/types/server/src/shared/account-routes.d.ts +153 -0
- package/dist/types/server/src/shared/api-versions.d.ts +13 -0
- package/dist/types/server/src/shared/cart-id.d.ts +2 -0
- package/dist/types/server/src/shared/error-message.d.ts +2 -0
- package/dist/types/server/src/shared/json.d.ts +25 -0
- package/dist/types/server/src/shared/market-codes.d.ts +16 -0
- package/dist/types/src/account/addresses-view.d.ts +5 -0
- package/dist/types/src/account/answers.d.ts +40 -0
- package/dist/types/src/account/markup.d.ts +19 -0
- package/dist/types/src/account/operations.d.ts +41 -0
- package/dist/types/src/account/order-lines.d.ts +13 -0
- package/dist/types/src/account/order-list.d.ts +12 -0
- package/dist/types/src/account/order-page.d.ts +6 -0
- package/dist/types/src/account/page-answers.d.ts +182 -0
- package/dist/types/src/account/page-events.d.ts +2 -0
- package/dist/types/src/account/server-client.d.ts +60 -0
- package/dist/types/src/account/subscriptions-view.d.ts +2 -0
- package/dist/types/src/account-api.d.ts +18 -0
- package/dist/types/src/account-marks.d.ts +15 -0
- package/dist/types/src/account-pages.d.ts +8 -0
- package/dist/types/src/account.d.ts +64 -0
- package/dist/types/src/amounts.d.ts +12 -0
- package/dist/types/src/analytics-shopify.d.ts +41 -0
- package/dist/types/src/analytics.d.ts +2 -0
- package/dist/types/src/api-version.d.ts +28 -0
- package/dist/types/src/astro.d.ts +42 -0
- package/dist/types/src/cart/cart-shape.d.ts +43 -0
- package/dist/types/src/cart/checkout.d.ts +16 -0
- package/dist/types/src/cart/drawer.d.ts +18 -0
- package/dist/types/src/cart/mutations.d.ts +85 -0
- package/dist/types/src/cart/render.d.ts +17 -0
- package/dist/types/src/cart/store.d.ts +185 -0
- package/dist/types/src/cart.d.ts +106 -0
- package/dist/types/src/client.d.ts +134 -0
- package/dist/types/src/collection.d.ts +25 -0
- package/dist/types/src/config-sections.d.ts +40 -0
- package/dist/types/src/config.d.ts +190 -0
- package/dist/types/src/controls/cart-controls.d.ts +2 -0
- package/dist/types/src/controls/discount.d.ts +2 -0
- package/dist/types/src/controls/extras.d.ts +5 -0
- package/dist/types/src/controls/navigation.d.ts +12 -0
- package/dist/types/src/diagnose-markup.d.ts +19 -0
- package/dist/types/src/diagnose.d.ts +60 -0
- package/dist/types/src/dom.d.ts +75 -0
- package/dist/types/src/extras.d.ts +21 -0
- package/dist/types/src/fragments.d.ts +158 -0
- package/dist/types/src/globals.d.ts +15 -0
- package/dist/types/src/images.d.ts +20 -0
- package/dist/types/src/index.d.ts +167 -0
- package/dist/types/src/initial.d.ts +56 -0
- package/dist/types/src/lazy.d.ts +28 -0
- package/dist/types/src/loader.d.ts +84 -0
- package/dist/types/src/market-codes.d.ts +6 -0
- package/dist/types/src/markets-ui.d.ts +32 -0
- package/dist/types/src/markets.d.ts +72 -0
- package/dist/types/src/markup.d.ts +131 -0
- package/dist/types/src/mounting.d.ts +46 -0
- package/dist/types/src/page-market.d.ts +27 -0
- package/dist/types/src/presets/barba.d.ts +56 -0
- package/dist/types/src/product-events.d.ts +2 -0
- package/dist/types/src/product-render.d.ts +17 -0
- package/dist/types/src/product-shape.d.ts +90 -0
- package/dist/types/src/product-state.d.ts +110 -0
- package/dist/types/src/product.d.ts +66 -0
- package/dist/types/src/repeaters.d.ts +16 -0
- package/dist/types/src/search.d.ts +33 -0
- package/dist/types/src/selling-plans.d.ts +64 -0
- package/dist/types/src/server.d.ts +88 -0
- package/dist/types/src/shopify-values.d.ts +46 -0
- package/dist/types/src/status.d.ts +33 -0
- package/dist/types/src/storage.d.ts +12 -0
- package/dist/types/src/store-html.d.ts +39 -0
- package/dist/types/src/storefront/catalog.d.ts +38 -0
- package/dist/types/src/storefront/json-ld.d.ts +20 -0
- package/dist/types/src/variant-render.d.ts +14 -0
- package/package.json +24 -24
- package/dist/esm/chunks/account-C3NP6HFB.js +0 -1
- package/dist/esm/chunks/account-pages-WLK374FO.js +0 -1
- package/dist/esm/chunks/analytics-G3SAWMNL.js +0 -1
- package/dist/esm/chunks/chunk-27PJJZGC.js +0 -1
- package/dist/esm/chunks/chunk-2FSXS55U.js +0 -1
- package/dist/esm/chunks/chunk-FCSWQDVV.js +0 -1
- package/dist/esm/chunks/chunk-FOMSMIAM.js +0 -1
- package/dist/esm/chunks/chunk-GZJTCLLW.js +0 -1
- package/dist/esm/chunks/chunk-HEQILZVR.js +0 -1
- package/dist/esm/chunks/chunk-JOOEXYF3.js +0 -4
- package/dist/esm/chunks/chunk-MPTQYLSM.js +0 -1
- package/dist/esm/chunks/chunk-NEHZ37TU.js +0 -3
- package/dist/esm/chunks/chunk-V74KGEJF.js +0 -1
- package/dist/esm/chunks/chunk-VT4CDUJJ.js +0 -1
- package/dist/esm/chunks/chunk-YJLWX2HK.js +0 -1
- package/dist/esm/chunks/diagnose-6VVGZNTY.js +0 -14
- package/dist/esm/chunks/extras-GJVRDFM3.js +0 -1
- package/dist/esm/chunks/markets-ui-UNB4PTA7.js +0 -1
- package/dist/esm/chunks/search-OU75RNQL.js +0 -1
- package/src/account/addresses-view.js +0 -92
- package/src/account/browser-session.js +0 -359
- package/src/account/markup.js +0 -48
- package/src/account/operations.js +0 -180
- package/src/account/order-lines.js +0 -96
- package/src/account/order-list.js +0 -91
- package/src/account/order-page.js +0 -192
- package/src/account/page-events.js +0 -288
- package/src/account/server-client.js +0 -172
- package/src/account/shared.js +0 -111
- package/src/account/shopify-auth.js +0 -150
- package/src/account/subscriptions-view.js +0 -158
- package/src/account-api.js +0 -118
- package/src/account-links.js +0 -35
- package/src/account-pages.js +0 -89
- package/src/account.js +0 -342
- package/src/amounts.js +0 -36
- package/src/analytics-shopify.js +0 -241
- package/src/analytics.js +0 -352
- package/src/api-version.js +0 -85
- package/src/astro.js +0 -266
- package/src/cart/checkout.js +0 -97
- package/src/cart/drawer.js +0 -138
- package/src/cart/mutations.js +0 -234
- package/src/cart/render.js +0 -294
- package/src/cart/store.js +0 -326
- package/src/cart.js +0 -391
- package/src/client.js +0 -267
- package/src/collection.js +0 -183
- package/src/config.js +0 -349
- package/src/controls/cart-controls.js +0 -155
- package/src/controls/discount.js +0 -96
- package/src/controls/extras.js +0 -63
- package/src/controls/navigation.js +0 -83
- package/src/diagnose-markup.js +0 -333
- package/src/diagnose.js +0 -295
- package/src/dom.js +0 -287
- package/src/extras.js +0 -145
- package/src/fragments.js +0 -237
- package/src/images.js +0 -104
- package/src/index.js +0 -353
- package/src/initial.js +0 -87
- package/src/lazy-iife.js +0 -116
- package/src/lazy.js +0 -39
- package/src/loader.js +0 -337
- package/src/market-codes.js +0 -35
- package/src/markets-ui.js +0 -204
- package/src/markets.js +0 -334
- package/src/markup.js +0 -64
- package/src/mounting.js +0 -277
- package/src/page-market.js +0 -116
- package/src/parts/account-pages.js +0 -6
- package/src/parts/account.js +0 -6
- package/src/parts/analytics.js +0 -6
- package/src/parts/diagnose.js +0 -6
- package/src/parts/extras.js +0 -6
- package/src/parts/markets.js +0 -6
- package/src/parts/search.js +0 -6
- package/src/presets/barba.js +0 -190
- package/src/product-events.js +0 -130
- package/src/product-render.js +0 -257
- package/src/product-state.js +0 -318
- package/src/product.js +0 -344
- package/src/repeaters.js +0 -173
- package/src/search.js +0 -323
- package/src/selling-plans.js +0 -154
- package/src/server.js +0 -215
- package/src/shopify-values.js +0 -89
- package/src/status.js +0 -126
- package/src/store-html.js +0 -0
- package/src/storefront/catalog.js +0 -199
- package/src/storefront/json-ld.js +0 -167
- package/src/variant-render.js +0 -165
- package/types/astro.d.ts +0 -28
- package/types/barba.d.ts +0 -16
- package/types/index.d.ts +0 -577
- package/types/server.d.ts +0 -182
package/dist/tether.account.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
(() => {
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
4
3
|
var __export = (target, all) => {
|
|
@@ -6,21 +5,18 @@
|
|
|
6
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
6
|
};
|
|
8
7
|
|
|
9
|
-
// src/account.
|
|
8
|
+
// src/account.ts
|
|
10
9
|
var account_exports = {};
|
|
11
10
|
__export(account_exports, {
|
|
12
11
|
addresses: () => addresses,
|
|
13
12
|
customer: () => customer,
|
|
14
13
|
deleteAddress: () => deleteAddress,
|
|
15
|
-
getToken: () => getToken,
|
|
16
|
-
handleCallback: () => handleCallback,
|
|
17
14
|
isLoggedIn: () => isLoggedIn,
|
|
18
15
|
login: () => login,
|
|
19
16
|
logout: () => logout,
|
|
20
17
|
mountAccount: () => mountAccount,
|
|
21
18
|
order: () => order,
|
|
22
19
|
orders: () => orders,
|
|
23
|
-
query: () => query2,
|
|
24
20
|
render: () => render,
|
|
25
21
|
requestReturn: () => requestReturn,
|
|
26
22
|
saveAddress: () => saveAddress,
|
|
@@ -29,412 +25,193 @@
|
|
|
29
25
|
updateProfile: () => updateProfile
|
|
30
26
|
});
|
|
31
27
|
|
|
32
|
-
// tether-core:config.
|
|
33
|
-
var m = window.Tether.__core["config.
|
|
34
|
-
var
|
|
28
|
+
// tether-core:config.ts
|
|
29
|
+
var m = window.Tether.__core["config.ts"];
|
|
30
|
+
var [cfg, isConfigured, log, MARKET_SOURCES, parseMarkets, reportError, warn] = m;
|
|
35
31
|
|
|
36
|
-
// tether-core:client.
|
|
37
|
-
var m2 = window.Tether.__core["client.
|
|
38
|
-
var
|
|
32
|
+
// tether-core:client.ts
|
|
33
|
+
var m2 = window.Tether.__core["client.ts"];
|
|
34
|
+
var [endpoint, gql, orNull, shaped, TetherError] = m2;
|
|
39
35
|
|
|
40
|
-
// tether-core:dom.
|
|
41
|
-
var m3 = window.Tether.__core["dom.
|
|
42
|
-
var
|
|
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;
|
|
43
39
|
|
|
44
|
-
// tether-core:cart.
|
|
45
|
-
var m4 = window.Tether.__core["cart.
|
|
46
|
-
var
|
|
40
|
+
// tether-core:cart.ts
|
|
41
|
+
var m4 = window.Tether.__core["cart.ts"];
|
|
42
|
+
var [Cart] = m4;
|
|
47
43
|
|
|
48
|
-
// tether-core:lazy.
|
|
49
|
-
var m5 = window.Tether.__core["lazy.
|
|
50
|
-
var
|
|
44
|
+
// tether-core:lazy.ts
|
|
45
|
+
var m5 = window.Tether.__core["lazy.ts"];
|
|
46
|
+
var [loadPart] = m5;
|
|
51
47
|
|
|
52
|
-
// server/src/shared/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
function toBase64Url(bytes) {
|
|
59
|
-
return btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
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;
|
|
60
54
|
}
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
return Uint8Array.from(atob(base64), (char) => char.charCodeAt(0));
|
|
55
|
+
function asList(value) {
|
|
56
|
+
return Array.isArray(value) ? value : [];
|
|
64
57
|
}
|
|
65
|
-
function
|
|
66
|
-
return
|
|
58
|
+
function textOrNull(value) {
|
|
59
|
+
return typeof value === "string" && value !== "" ? value : null;
|
|
67
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
68
|
|
|
69
|
-
// src/
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
var hintKey = () => `${cfg.prefix}:account-hint:${cfg.domain}`;
|
|
73
|
-
var sessionArea = () => cfg.account?.remember ? "local" : "session";
|
|
74
|
-
var storageOf = (area) => area === "local" ? window.localStorage : window.sessionStorage;
|
|
75
|
-
function readJsonIfAllowed(area, key) {
|
|
76
|
-
try {
|
|
77
|
-
return JSON.parse(storageOf(area).getItem(key) ?? "null");
|
|
78
|
-
} catch {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
69
|
+
// server/src/shared/error-message.ts
|
|
70
|
+
function messageOf(error) {
|
|
71
|
+
return error instanceof Error ? error.message : String(error);
|
|
81
72
|
}
|
|
82
|
-
|
|
73
|
+
|
|
74
|
+
// src/storage.ts
|
|
75
|
+
function setStored(key, value, area = "localStorage") {
|
|
83
76
|
try {
|
|
84
|
-
|
|
85
|
-
else storageOf(area).setItem(key, JSON.stringify(value));
|
|
77
|
+
window[area].setItem(key, value);
|
|
86
78
|
} catch {
|
|
87
79
|
}
|
|
88
80
|
}
|
|
89
|
-
function
|
|
90
|
-
writeJsonIfAllowed("local", hintKey(), loggedIn ? 1 : null);
|
|
91
|
-
}
|
|
92
|
-
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
93
|
-
var textOrNull = (value) => typeof value === "string" ? value : null;
|
|
94
|
-
async function readJsonObjectOrEmpty(response) {
|
|
95
|
-
let json;
|
|
81
|
+
function removeStored(key, area = "localStorage") {
|
|
96
82
|
try {
|
|
97
|
-
|
|
83
|
+
window[area].removeItem(key);
|
|
98
84
|
} catch {
|
|
99
|
-
return {};
|
|
100
85
|
}
|
|
101
|
-
return isRecord(json) ? json : {};
|
|
102
|
-
}
|
|
103
|
-
function navigate(url) {
|
|
104
|
-
const replacement = cfg.account?.navigate;
|
|
105
|
-
if (replacement) replacement(url);
|
|
106
|
-
else window.location.assign(url);
|
|
107
86
|
}
|
|
108
|
-
var loginLanguage = (locale) => locale || cfg.account?.locale || (cfg.locale || "").split("-")[0] || "";
|
|
109
|
-
var loginCountry = () => cfg.country ? cfg.country.toUpperCase() : "";
|
|
110
87
|
|
|
111
|
-
// src/account
|
|
112
|
-
var
|
|
113
|
-
var
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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));
|
|
119
124
|
}
|
|
120
|
-
|
|
121
|
-
/** @type {unknown} */
|
|
122
|
-
await response.json()
|
|
123
|
-
);
|
|
124
|
-
return isRecord(json) ? json : {};
|
|
125
|
+
return { kind: "read", orders: orders2 };
|
|
125
126
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const
|
|
134
|
-
endpoints = {
|
|
135
|
-
issuer: endpointIn(openid, "issuer"),
|
|
136
|
-
authorizationEndpoint: endpointIn(openid, "authorization_endpoint"),
|
|
137
|
-
tokenEndpoint: endpointIn(openid, "token_endpoint"),
|
|
138
|
-
endSessionEndpoint: endpointIn(openid, "end_session_endpoint"),
|
|
139
|
-
graphql: endpointIn(api, "graphql_api")
|
|
140
|
-
};
|
|
141
|
-
return endpoints;
|
|
142
|
-
}
|
|
143
|
-
async function requestToken(params) {
|
|
144
|
-
const { tokenEndpoint } = await discover();
|
|
145
|
-
const response = await fetch(cfg.account?.tokenUrl || tokenEndpoint, {
|
|
146
|
-
method: "POST",
|
|
147
|
-
body: new URLSearchParams(params)
|
|
148
|
-
});
|
|
149
|
-
const data = await readJsonObjectOrEmpty(response);
|
|
150
|
-
const access = textOrNull(data["access_token"]);
|
|
151
|
-
if (!response.ok || !access) {
|
|
152
|
-
const ended = response.status === 400 || response.status === 401;
|
|
153
|
-
throw new TetherError(`Customer accounts: token ${response.status} ${textOrNull(data["error"]) ?? ""}`.trim(), {
|
|
154
|
-
status: response.status,
|
|
155
|
-
code: ended ? "SESSION_ENDED" : "TOKEN_FAILED"
|
|
156
|
-
});
|
|
157
|
-
}
|
|
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"];
|
|
158
135
|
return {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
|
163
142
|
};
|
|
164
143
|
}
|
|
165
|
-
function
|
|
166
|
-
const
|
|
167
|
-
if (
|
|
168
|
-
|
|
169
|
-
try {
|
|
170
|
-
claims = JSON.parse(new TextDecoder().decode(fromBase64Url(payload)));
|
|
171
|
-
} catch {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
174
|
-
return isRecord(claims) ? claims : null;
|
|
175
|
-
}
|
|
176
|
-
function answersLogin(idToken, { nonce, issuer, client }) {
|
|
177
|
-
const claims = idToken ? idTokenClaims(idToken) : null;
|
|
178
|
-
if (!claims) return false;
|
|
179
|
-
const audience = claims["aud"];
|
|
180
|
-
const audiences = Array.isArray(audience) ? audience : [audience];
|
|
181
|
-
const expiresAt = claims["exp"];
|
|
182
|
-
return claims["nonce"] === nonce && claims["iss"] === issuer && audiences.includes(client) && typeof expiresAt === "number" && expiresAt * 1e3 >= Date.now();
|
|
144
|
+
function toAccountOrders(value) {
|
|
145
|
+
const reading = readOrders(value);
|
|
146
|
+
if (reading.kind === "missing-id") throw unexpected("order id");
|
|
147
|
+
return reading.orders;
|
|
183
148
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if (!isRecord(value)) return null;
|
|
193
|
-
const access = value["access"];
|
|
194
|
-
const expiresAt = value["expiresAt"];
|
|
195
|
-
if (typeof access !== "string" || typeof expiresAt !== "number") return null;
|
|
196
|
-
return { access, expiresAt, refresh: textOrNull(value["refresh"]), idToken: textOrNull(value["idToken"]) };
|
|
197
|
-
}
|
|
198
|
-
function toPendingLogin(value) {
|
|
199
|
-
if (!isRecord(value)) return null;
|
|
200
|
-
const { state, nonce, verifier, redirect, returnTo, at } = value;
|
|
201
|
-
if (typeof state !== "string" || typeof nonce !== "string" || typeof verifier !== "string") return null;
|
|
202
|
-
if (typeof redirect !== "string" || typeof returnTo !== "string" || typeof at !== "number") return null;
|
|
203
|
-
return { state, nonce, verifier, redirect, returnTo, at };
|
|
204
|
-
}
|
|
205
|
-
var currentSession = () => session ?? toSession(readJsonIfAllowed(sessionArea(), sessionKey()));
|
|
206
|
-
var accessToken = () => session?.access ?? null;
|
|
207
|
-
var hasBrowserLogin = () => Boolean(session && (session.refresh || session.access && session.expiresAt > Date.now()));
|
|
208
|
-
function clientId() {
|
|
209
|
-
const id = cfg.account?.clientId;
|
|
210
|
-
if (!id) throw new TetherError("Customer accounts: set account.clientId", { code: "NOT_CONFIGURED" });
|
|
211
|
-
return id;
|
|
212
|
-
}
|
|
213
|
-
var sha256 = async (text) => toBase64Url(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text)));
|
|
214
|
-
async function browserLoginRedirect({ returnTo, locale }) {
|
|
215
|
-
const client = clientId();
|
|
216
|
-
const endpoints2 = await discover();
|
|
217
|
-
const pending = {
|
|
218
|
-
state: randomToken(),
|
|
219
|
-
nonce: randomToken(),
|
|
220
|
-
verifier: randomToken(),
|
|
221
|
-
redirect: cfg.account?.callbackUrl || `${window.location.origin}${window.location.pathname}`,
|
|
222
|
-
returnTo,
|
|
223
|
-
at: Date.now()
|
|
224
|
-
};
|
|
225
|
-
writeJsonIfAllowed("session", pendingLoginKey(), pending);
|
|
226
|
-
const url = new URL(endpoints2.authorizationEndpoint);
|
|
227
|
-
const params = {
|
|
228
|
-
client_id: client,
|
|
229
|
-
scope: "openid email customer-account-api:full",
|
|
230
|
-
response_type: "code",
|
|
231
|
-
redirect_uri: pending.redirect,
|
|
232
|
-
state: pending.state,
|
|
233
|
-
nonce: pending.nonce,
|
|
234
|
-
code_challenge: await sha256(pending.verifier),
|
|
235
|
-
code_challenge_method: "S256"
|
|
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"))
|
|
236
157
|
};
|
|
237
|
-
const language = loginLanguage(locale);
|
|
238
|
-
if (language) params["locale"] = language;
|
|
239
|
-
const country = loginCountry();
|
|
240
|
-
if (country) params["region_country"] = country;
|
|
241
|
-
Object.entries(params).forEach(([name, value]) => {
|
|
242
|
-
url.searchParams.set(name, value);
|
|
243
|
-
});
|
|
244
|
-
navigate(url.toString());
|
|
245
158
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
}
|
|
255
|
-
async function handleCallback() {
|
|
256
|
-
const params = new URLSearchParams(window.location.search);
|
|
257
|
-
const pending = toPendingLogin(readJsonIfAllowed("session", pendingLoginKey()));
|
|
258
|
-
if (!pending || !params.has("code") && !params.has("error") || params.get("state") !== pending.state) return false;
|
|
259
|
-
writeJsonIfAllowed("session", pendingLoginKey(), null);
|
|
260
|
-
const clean = new URL(window.location.href);
|
|
261
|
-
["code", "state", "iss", "error", "error_description"].forEach((name) => {
|
|
262
|
-
clean.searchParams.delete(name);
|
|
263
|
-
});
|
|
264
|
-
window.history.replaceState(window.history.state, "", clean);
|
|
265
|
-
const error = params.get("error");
|
|
266
|
-
if (error !== null) {
|
|
267
|
-
emit("account-error", { error });
|
|
268
|
-
return false;
|
|
269
|
-
}
|
|
270
|
-
if (Date.now() - pending.at > LOGIN_TIMEOUT_MS) {
|
|
271
|
-
throw new TetherError("Customer accounts: the login took too long, please try again");
|
|
272
|
-
}
|
|
273
|
-
const client = clientId();
|
|
274
|
-
const answer = await requestToken({
|
|
275
|
-
grant_type: "authorization_code",
|
|
276
|
-
client_id: client,
|
|
277
|
-
redirect_uri: pending.redirect,
|
|
278
|
-
code: params.get("code") ?? "",
|
|
279
|
-
code_verifier: pending.verifier
|
|
280
|
-
});
|
|
281
|
-
const { issuer } = await discover();
|
|
282
|
-
if (!answersLogin(answer.idToken, { nonce: pending.nonce, issuer, client })) {
|
|
283
|
-
throw new TetherError("Customer accounts: the login answer did not check out (nonce/issuer/audience)");
|
|
284
|
-
}
|
|
285
|
-
keep(answer, null);
|
|
286
|
-
await linkCart();
|
|
287
|
-
emit("account-login", {});
|
|
288
|
-
const back = pending.returnTo ? new URL(pending.returnTo, window.location.href) : null;
|
|
289
|
-
if (back?.origin === window.location.origin && back.href !== clean.href) navigate(back.href);
|
|
290
|
-
return true;
|
|
291
|
-
}
|
|
292
|
-
function endSession() {
|
|
293
|
-
session = null;
|
|
294
|
-
customerProfile = null;
|
|
295
|
-
writeJsonIfAllowed(sessionArea(), sessionKey(), null);
|
|
296
|
-
Cart.setCustomer({ accessToken: null });
|
|
297
|
-
}
|
|
298
|
-
async function refresh(expired) {
|
|
299
|
-
try {
|
|
300
|
-
const answer = await requestToken({
|
|
301
|
-
grant_type: "refresh_token",
|
|
302
|
-
client_id: clientId(),
|
|
303
|
-
refresh_token: expired.refresh ?? ""
|
|
304
|
-
});
|
|
305
|
-
keep(answer, expired);
|
|
306
|
-
await linkCart();
|
|
307
|
-
} catch (error) {
|
|
308
|
-
if (error instanceof TetherError && error.code === "SESSION_ENDED") endSession();
|
|
309
|
-
else throw error;
|
|
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" });
|
|
310
167
|
}
|
|
168
|
+
return new URL(server, window.location.href).href.replace(/\/$/, "");
|
|
311
169
|
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (session.access && session.expiresAt > Date.now()) return session.access;
|
|
316
|
-
if (!session.refresh) {
|
|
317
|
-
endSession();
|
|
318
|
-
return null;
|
|
319
|
-
}
|
|
320
|
-
const expired = session;
|
|
321
|
-
refreshing ?? (refreshing = refresh(expired).finally(() => {
|
|
322
|
-
refreshing = null;
|
|
323
|
-
}));
|
|
324
|
-
await refreshing;
|
|
325
|
-
return accessToken();
|
|
170
|
+
function setLoginHint(loggedIn) {
|
|
171
|
+
if (loggedIn) setStored(hintKey(), "1");
|
|
172
|
+
else removeStored(hintKey());
|
|
326
173
|
}
|
|
327
|
-
async function
|
|
328
|
-
const token = accessToken();
|
|
329
|
-
Cart.setCustomer({ accessToken: token });
|
|
330
|
-
if (!Cart.id() || !token) return;
|
|
174
|
+
async function fetchFromWorker(url, init) {
|
|
331
175
|
try {
|
|
332
|
-
await
|
|
176
|
+
return await fetch(url, init);
|
|
333
177
|
} catch (error) {
|
|
334
|
-
|
|
178
|
+
throw new TetherError(`Network error: ${messageOf(error)}`, {
|
|
179
|
+
code: "NETWORK"
|
|
180
|
+
});
|
|
335
181
|
}
|
|
336
182
|
}
|
|
337
|
-
async function
|
|
338
|
-
|
|
183
|
+
async function readJsonObjectOrEmpty(response) {
|
|
184
|
+
let json;
|
|
339
185
|
try {
|
|
340
|
-
await
|
|
341
|
-
} catch
|
|
342
|
-
|
|
343
|
-
writeJsonIfAllowed("local", cfg.storageKey, null);
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
async function browserLogout(returnTo) {
|
|
347
|
-
const idToken = currentSession()?.idToken ?? null;
|
|
348
|
-
await unlinkCart();
|
|
349
|
-
endSession();
|
|
350
|
-
emit("account-logout", {});
|
|
351
|
-
if (!idToken) return "stayed";
|
|
352
|
-
const url = new URL((await discover()).endSessionEndpoint);
|
|
353
|
-
url.searchParams.set("id_token_hint", idToken);
|
|
354
|
-
url.searchParams.set("post_logout_redirect_uri", returnTo);
|
|
355
|
-
navigate(url.toString());
|
|
356
|
-
return "redirected";
|
|
357
|
-
}
|
|
358
|
-
async function query(document2, variables = {}) {
|
|
359
|
-
const token = await getToken();
|
|
360
|
-
if (!token) throw new TetherError("Not logged in", { code: "NOT_LOGGED_IN" });
|
|
361
|
-
const { graphql } = await discover();
|
|
362
|
-
const send = (accessToken2) => fetch(graphql, {
|
|
363
|
-
method: "POST",
|
|
364
|
-
headers: { "Content-Type": "application/json", Authorization: accessToken2 },
|
|
365
|
-
body: JSON.stringify({ query: document2, variables })
|
|
366
|
-
});
|
|
367
|
-
let response = await send(token);
|
|
368
|
-
if (response.status === 401 && session?.refresh) {
|
|
369
|
-
session.expiresAt = 0;
|
|
370
|
-
response = await send(await getToken() ?? "");
|
|
371
|
-
}
|
|
372
|
-
const json = await readJsonObjectOrEmpty(response);
|
|
373
|
-
const errors = Array.isArray(json["errors"]) ? json["errors"] : [];
|
|
374
|
-
if (!response.ok || errors.length) {
|
|
375
|
-
const messages = errors.map((error) => isRecord(error) ? textOrNull(error["message"]) : null).filter(Boolean);
|
|
376
|
-
throw new TetherError(messages.join(" | ") || `Customer accounts: ${response.status}`, {
|
|
377
|
-
status: response.status
|
|
378
|
-
});
|
|
186
|
+
json = await response.json();
|
|
187
|
+
} catch {
|
|
188
|
+
return {};
|
|
379
189
|
}
|
|
380
|
-
return (
|
|
381
|
-
/** @type {T} */
|
|
382
|
-
json["data"]
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
async function browserCustomer() {
|
|
386
|
-
if (customerProfile) return customerProfile;
|
|
387
|
-
const data = await query(CUSTOMER_QUERY);
|
|
388
|
-
customerProfile = data.customer;
|
|
389
|
-
return customerProfile;
|
|
390
|
-
}
|
|
391
|
-
function forgetBrowserCustomer() {
|
|
392
|
-
customerProfile = null;
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// src/account/server-client.js
|
|
396
|
-
var known = null;
|
|
397
|
-
function serverBase() {
|
|
398
|
-
const server = cfg.account?.server;
|
|
399
|
-
if (!server) return "";
|
|
400
|
-
return new URL(server, window.location.href).href.replace(/\/$/, "");
|
|
190
|
+
return isRecord(json) ? json : {};
|
|
401
191
|
}
|
|
402
192
|
async function serverFetch(path, { method = "GET", body } = {}) {
|
|
403
193
|
const init = { method, credentials: "include" };
|
|
404
194
|
if (method === "POST") init.headers = { "Content-Type": "application/json", "X-Tether-Account": "1" };
|
|
405
195
|
if (body) init.body = JSON.stringify(body);
|
|
406
|
-
const response = await
|
|
196
|
+
const response = await fetchFromWorker(`${serverBase()}${path}`, init);
|
|
407
197
|
if (response.status === 401) return { kind: "logged-out" };
|
|
408
198
|
const data = await readJsonObjectOrEmpty(response);
|
|
409
199
|
if (!response.ok) {
|
|
410
200
|
const reason = textOrNull(data["error"]) ?? String(response.status);
|
|
411
|
-
const userErrors =
|
|
412
|
-
// The Worker sends Shopify's own `userErrors` list unchanged.
|
|
413
|
-
/** @type {UserError[]} */
|
|
414
|
-
data["userErrors"]
|
|
415
|
-
) : void 0;
|
|
201
|
+
const userErrors = toUserErrors(data["userErrors"]);
|
|
416
202
|
throw new TetherError(`Customer accounts: ${reason}`, {
|
|
417
203
|
status: response.status,
|
|
418
|
-
...userErrors && { userErrors }
|
|
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" }
|
|
419
207
|
});
|
|
420
208
|
}
|
|
421
209
|
return { kind: "answer", data };
|
|
422
210
|
}
|
|
423
|
-
function toServerCustomer(value) {
|
|
424
|
-
const customer2 = isRecord(value) ? value : {};
|
|
425
|
-
const address = customer2["address"];
|
|
426
|
-
return {
|
|
427
|
-
firstName: textOrNull(customer2["firstName"]),
|
|
428
|
-
lastName: textOrNull(customer2["lastName"]),
|
|
429
|
-
displayName: textOrNull(customer2["displayName"]),
|
|
430
|
-
email: textOrNull(customer2["email"]),
|
|
431
|
-
address: Array.isArray(address) ? address.filter((line) => typeof line === "string") : null
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
211
|
async function serverLogin() {
|
|
435
212
|
if (!known) {
|
|
436
213
|
const answer = await serverFetch("/me");
|
|
437
|
-
known = answer.kind === "answer" && answer.data["loggedIn"] === true ? { loggedIn: true, customer:
|
|
214
|
+
known = answer.kind === "answer" && answer.data["loggedIn"] === true ? { loggedIn: true, customer: toAccountCustomer(answer.data["customer"]) } : { loggedIn: false };
|
|
438
215
|
}
|
|
439
216
|
setLoginHint(known.loggedIn);
|
|
440
217
|
return known;
|
|
@@ -443,13 +220,18 @@
|
|
|
443
220
|
function forgetServerLogin() {
|
|
444
221
|
known = null;
|
|
445
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] || "";
|
|
446
229
|
function serverLoginRedirect({ returnTo, locale }) {
|
|
447
230
|
const url = new URL(`${serverBase()}/login`);
|
|
448
231
|
url.searchParams.set("returnTo", returnTo);
|
|
449
232
|
const language = loginLanguage(locale);
|
|
450
233
|
if (language) url.searchParams.set("locale", language);
|
|
451
|
-
|
|
452
|
-
if (country) url.searchParams.set("country", country);
|
|
234
|
+
if (cfg.country) url.searchParams.set("country", cfg.country.toUpperCase());
|
|
453
235
|
navigate(url.toString());
|
|
454
236
|
}
|
|
455
237
|
async function serverCheckoutUrl() {
|
|
@@ -457,9 +239,7 @@
|
|
|
457
239
|
if (!(await serverLogin()).loggedIn || !cartId) return null;
|
|
458
240
|
const answer = await serverFetch("/cart", { method: "POST", body: { cartId } });
|
|
459
241
|
if (answer.kind === "logged-out") return null;
|
|
460
|
-
|
|
461
|
-
if (!url) return null;
|
|
462
|
-
return `${url}${url.includes("?") ? "&" : "?"}logged_in=true`;
|
|
242
|
+
return textOrNull(answer.data["checkoutUrl"]);
|
|
463
243
|
}
|
|
464
244
|
async function serverLogout(returnTo) {
|
|
465
245
|
const answer = await serverFetch("/logout", { method: "POST", body: { returnTo, cartId: Cart.id() } });
|
|
@@ -472,29 +252,11 @@
|
|
|
472
252
|
return "redirected";
|
|
473
253
|
}
|
|
474
254
|
|
|
475
|
-
// tether-core:shopify-values.
|
|
476
|
-
var m6 = window.Tether.__core["shopify-values.
|
|
477
|
-
var
|
|
478
|
-
|
|
479
|
-
// src/account-links.js
|
|
480
|
-
function localDate(iso) {
|
|
481
|
-
try {
|
|
482
|
-
return new Date(iso).toLocaleDateString(cfg.locale);
|
|
483
|
-
} catch (error) {
|
|
484
|
-
if (error instanceof RangeError) return iso;
|
|
485
|
-
throw error;
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
function orderUrl(order2) {
|
|
489
|
-
const template = cfg.account?.orderUrl;
|
|
490
|
-
if (template) {
|
|
491
|
-
const numericId = order2.id.split("/").pop()?.split("?")[0] ?? "";
|
|
492
|
-
return template.replace("{id}", encodeURIComponent(numericId));
|
|
493
|
-
}
|
|
494
|
-
return isHttpsUrl(order2.statusPageUrl) ? order2.statusPageUrl ?? null : null;
|
|
495
|
-
}
|
|
255
|
+
// tether-core:shopify-values.ts
|
|
256
|
+
var m6 = window.Tether.__core["shopify-values.ts"];
|
|
257
|
+
var [isHttpsUrl] = m6;
|
|
496
258
|
|
|
497
|
-
// src/account/markup.
|
|
259
|
+
// src/account/markup.ts
|
|
498
260
|
function showIn(root, name, conditions) {
|
|
499
261
|
qsa(root, sel(name)).forEach((el) => {
|
|
500
262
|
const key = get(el, name) ?? "";
|
|
@@ -504,9 +266,9 @@
|
|
|
504
266
|
var bindsIn = (row, name) => qsa(row, sel(name)).concat(row.matches(sel(name)) ? [row] : []);
|
|
505
267
|
var ownValue = (values, key) => Object.hasOwn(values, key) ? values[key] : void 0;
|
|
506
268
|
|
|
507
|
-
// src/account/order-lines.
|
|
269
|
+
// src/account/order-lines.ts
|
|
508
270
|
var lineTexts = (line, returnable) => ({
|
|
509
|
-
title: line.title || line.name,
|
|
271
|
+
title: line.title || line.name || "",
|
|
510
272
|
"variant-title": line.variantTitle || "",
|
|
511
273
|
quantity: String(line.quantity),
|
|
512
274
|
price: formatMoney(line.price),
|
|
@@ -528,7 +290,7 @@
|
|
|
528
290
|
if (select.dataset["tetherFilled"] === line.id) return;
|
|
529
291
|
select.dataset["tetherFilled"] = line.id;
|
|
530
292
|
(line.suggestedReturnReasonDefinitions?.nodes ?? []).forEach((reason) => {
|
|
531
|
-
select.appendChild(new Option(reason.name, reason.id));
|
|
293
|
+
select.appendChild(new Option(reason.name ?? "", reason.id));
|
|
532
294
|
});
|
|
533
295
|
});
|
|
534
296
|
}
|
|
@@ -541,7 +303,7 @@
|
|
|
541
303
|
bindsIn(row, "order-line").forEach((el) => {
|
|
542
304
|
const key = get(el, "order-line") ?? "";
|
|
543
305
|
if (key === "image") {
|
|
544
|
-
if (line.image) setImage(el, line.image.url, line.image.altText || line.title);
|
|
306
|
+
if (line.image) setImage(el, line.image.url, line.image.altText || line.title || "");
|
|
545
307
|
return;
|
|
546
308
|
}
|
|
547
309
|
const text = ownValue(texts, key);
|
|
@@ -551,15 +313,28 @@
|
|
|
551
313
|
});
|
|
552
314
|
}
|
|
553
315
|
|
|
554
|
-
// src/account/order-list.
|
|
316
|
+
// src/account/order-list.ts
|
|
555
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
|
+
}
|
|
556
331
|
var orderTexts = (order2) => ({
|
|
557
|
-
name: order2.name,
|
|
558
|
-
date:
|
|
332
|
+
name: order2.name ?? "",
|
|
333
|
+
date: formatDate(order2.processedAt),
|
|
559
334
|
total: formatMoney(order2.totalPrice),
|
|
560
335
|
status: order2.financialStatus?.toLowerCase(),
|
|
561
336
|
fulfillment: order2.fulfillmentStatus?.toLowerCase(),
|
|
562
|
-
items: order2.lineItems.nodes.map((line) => `${line.quantity}\xD7 ${line.title}`).join(", ")
|
|
337
|
+
items: order2.lineItems.nodes.map((line) => `${String(line.quantity)}\xD7 ${line.title ?? ""}`).join(", ")
|
|
563
338
|
});
|
|
564
339
|
function fillOrder(node, order2) {
|
|
565
340
|
const lines = order2.lineItems.nodes;
|
|
@@ -589,80 +364,70 @@
|
|
|
589
364
|
const first = lists[0];
|
|
590
365
|
if (!first) return;
|
|
591
366
|
showIn(root, "show", { "orders-loading": true, "orders-empty": false });
|
|
592
|
-
const limit =
|
|
593
|
-
|
|
367
|
+
const limit = listLimit(get(first, "limit"));
|
|
368
|
+
let orders2;
|
|
369
|
+
try {
|
|
370
|
+
orders2 = await loadOrders(limit);
|
|
371
|
+
} catch (error) {
|
|
594
372
|
reportError(error, "orders");
|
|
595
|
-
|
|
596
|
-
}
|
|
373
|
+
orders2 = null;
|
|
374
|
+
}
|
|
597
375
|
showIn(root, "show", { "orders-loading": false, "orders-empty": orders2?.length === 0 });
|
|
598
376
|
lists.forEach((list) => {
|
|
599
377
|
repeat(list, orders2 ?? [], fillOrder);
|
|
600
378
|
});
|
|
601
379
|
}
|
|
602
380
|
|
|
603
|
-
// src/account.
|
|
381
|
+
// src/account.ts
|
|
604
382
|
var PAGE_ATTRIBUTES = ["order", "addresses", "subscriptions", "profile-form", "address-form"];
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
throw new TetherError("Customer accounts in server mode only answer customer() and orders()", {
|
|
615
|
-
code: "NOT_ALLOWED"
|
|
616
|
-
});
|
|
617
|
-
}
|
|
618
|
-
return query(document2, variables);
|
|
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();
|
|
391
|
+
});
|
|
619
392
|
}
|
|
620
393
|
async function customer() {
|
|
621
|
-
if (!serverBase()) return browserCustomer();
|
|
622
394
|
const me = await serverLogin();
|
|
623
|
-
|
|
624
|
-
return {
|
|
625
|
-
...me.customer,
|
|
626
|
-
emailAddress: { emailAddress: me.customer.email },
|
|
627
|
-
defaultAddress: { formatted: me.customer.address }
|
|
628
|
-
};
|
|
395
|
+
return me.loggedIn ? me.customer : null;
|
|
629
396
|
}
|
|
630
|
-
async function orders({ first =
|
|
631
|
-
if (
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
return Array.isArray(list) ? (
|
|
636
|
-
/** @type {AccountOrder[]} */
|
|
637
|
-
list
|
|
638
|
-
) : [];
|
|
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"
|
|
401
|
+
});
|
|
639
402
|
}
|
|
640
|
-
const
|
|
641
|
-
return
|
|
403
|
+
const answer = await serverFetch(`/orders?${new URLSearchParams({ first: String(first) }).toString()}`);
|
|
404
|
+
return answer.kind === "logged-out" ? [] : toAccountOrders(answer.data["orders"]);
|
|
642
405
|
}
|
|
643
|
-
async function logout({
|
|
644
|
-
|
|
645
|
-
|
|
406
|
+
async function logout({
|
|
407
|
+
returnTo = cfg.account?.logoutUrl || `${window.location.origin}/`
|
|
408
|
+
} = {}) {
|
|
409
|
+
if (await serverLogout(returnTo) === "stayed") await render(document);
|
|
646
410
|
}
|
|
647
411
|
async function loginState() {
|
|
648
412
|
try {
|
|
649
|
-
|
|
650
|
-
if (hasBrowserLogin() || await getToken()) return { kind: "logged-in" };
|
|
651
|
-
return { kind: "logged-out" };
|
|
413
|
+
return (await serverLogin()).loggedIn ? { kind: "logged-in" } : { kind: "logged-out" };
|
|
652
414
|
} catch (error) {
|
|
653
415
|
return { kind: "unknown", error };
|
|
654
416
|
}
|
|
655
417
|
}
|
|
656
418
|
var usesPages = (root) => PAGE_ATTRIBUTES.some((name) => qsa(root, sel(name)).length > 0);
|
|
657
419
|
function linkShopifyPages(root) {
|
|
658
|
-
const account = cfg.account;
|
|
659
420
|
qsa(root, sel("account-link")).forEach((link) => {
|
|
660
|
-
|
|
661
|
-
const subscriptions2 = account?.subscriptionsUrl;
|
|
662
|
-
const url = page === "payment" && (account?.paymentUrl || subscriptions2) || page === "subscriptions" && subscriptions2;
|
|
663
|
-
link.setAttribute("href", url || `https://${cfg.domain}/account`);
|
|
421
|
+
link.setAttribute("href", shopifyPageUrl(get(link, "account-link")));
|
|
664
422
|
});
|
|
665
423
|
}
|
|
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
|
+
}
|
|
666
431
|
async function render(root = document) {
|
|
667
432
|
const state = await loginState();
|
|
668
433
|
if (state.kind === "unknown") reportError(state.error, "account");
|
|
@@ -687,16 +452,20 @@
|
|
|
687
452
|
async function bindCustomer(root) {
|
|
688
453
|
const binds = qsa(root, sel("account-bind"));
|
|
689
454
|
if (!binds.length) return;
|
|
690
|
-
|
|
455
|
+
let profile;
|
|
456
|
+
try {
|
|
457
|
+
profile = await customer();
|
|
458
|
+
} catch (error) {
|
|
691
459
|
reportError(error, "account");
|
|
692
|
-
return
|
|
693
|
-
}
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (!profile) return;
|
|
694
463
|
const texts = {
|
|
695
|
-
"first-name": profile
|
|
696
|
-
"last-name": profile
|
|
697
|
-
name: profile
|
|
698
|
-
email: profile
|
|
699
|
-
address: profile
|
|
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(", ")
|
|
700
469
|
};
|
|
701
470
|
binds.forEach((el) => {
|
|
702
471
|
const text = ownValue(texts, get(el, "account-bind") ?? "");
|
|
@@ -706,17 +475,7 @@
|
|
|
706
475
|
var pagesPart = null;
|
|
707
476
|
function pages() {
|
|
708
477
|
pagesPart ?? (pagesPart = loadPart("account-pages").then((part) => {
|
|
709
|
-
part.setup({
|
|
710
|
-
serverBase,
|
|
711
|
-
serverFetch,
|
|
712
|
-
query: query2,
|
|
713
|
-
customer,
|
|
714
|
-
bindCustomer,
|
|
715
|
-
reset: () => {
|
|
716
|
-
forgetBrowserCustomer();
|
|
717
|
-
forgetServerLogin();
|
|
718
|
-
}
|
|
719
|
-
});
|
|
478
|
+
part.setup({ serverFetch, customer, bindCustomer, reset: forgetServerLogin });
|
|
720
479
|
return part;
|
|
721
480
|
}));
|
|
722
481
|
return pagesPart;
|
|
@@ -744,46 +503,48 @@
|
|
|
744
503
|
});
|
|
745
504
|
}
|
|
746
505
|
}
|
|
747
|
-
function
|
|
506
|
+
function finishServerLogin() {
|
|
748
507
|
const url = new URL(window.location.href);
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
}
|
|
762
|
-
if (Cart.customerAccessToken) return;
|
|
763
|
-
try {
|
|
764
|
-
const accessToken2 = await getToken();
|
|
765
|
-
if (accessToken2) Cart.setCustomer({ accessToken: accessToken2 });
|
|
766
|
-
} catch (error) {
|
|
767
|
-
reportError(error, "account");
|
|
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;
|
|
768
520
|
}
|
|
769
521
|
}
|
|
770
522
|
var started = false;
|
|
771
523
|
async function mountAccount(root = document) {
|
|
772
|
-
if (
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
if (serverBase()) startServerMode();
|
|
776
|
-
else await startBrowserMode();
|
|
777
|
-
log("account", isLoggedIn() ? "logged in" : "logged out");
|
|
524
|
+
if (started) {
|
|
525
|
+
await render(root);
|
|
526
|
+
return;
|
|
778
527
|
}
|
|
528
|
+
started = true;
|
|
529
|
+
document.addEventListener("click", onClick);
|
|
530
|
+
finishServerLogin();
|
|
531
|
+
Cart.setCheckoutResolver(serverCheckoutUrl);
|
|
779
532
|
await render(root);
|
|
533
|
+
log("account", isLoggedIn() ? "logged in" : "logged out");
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// src/part-registry.ts
|
|
537
|
+
function isTetherInternals(value) {
|
|
538
|
+
return isRecord(value) && isRecord(value["__core"]) && isRecord(value["__parts"]);
|
|
539
|
+
}
|
|
540
|
+
function registerPart(name, part) {
|
|
541
|
+
const tether = Reflect.get(window, "Tether");
|
|
542
|
+
if (!isTetherInternals(tether)) {
|
|
543
|
+
throw new Error(`tether.${name}.js runs after the Tether script, which loads it, and needs its window.Tether`);
|
|
544
|
+
}
|
|
545
|
+
tether.__parts[name] = part;
|
|
780
546
|
}
|
|
781
547
|
|
|
782
|
-
// src/parts/account.
|
|
783
|
-
|
|
784
|
-
/** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */
|
|
785
|
-
window
|
|
786
|
-
);
|
|
787
|
-
if (!Tether) throw new Error("tether.account.js runs after the Tether script, which loads it");
|
|
788
|
-
Tether.__parts["account"] = account_exports;
|
|
548
|
+
// src/parts/account.ts
|
|
549
|
+
registerPart("account", account_exports);
|
|
789
550
|
})();
|