@occluze/tether 0.6.8 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -11
- package/dist/esm/astro.js +2 -2
- package/dist/esm/chunks/account-UIPWJQ3Y.js +1 -0
- package/dist/esm/chunks/account-pages-6ROA6Y4V.js +1 -0
- package/dist/esm/chunks/analytics-2V632IRV.js +1 -0
- package/dist/esm/chunks/chunk-4GCXM764.js +1 -0
- package/dist/esm/chunks/chunk-56FCLJJD.js +1 -0
- package/dist/esm/chunks/chunk-5Z5O3767.js +1 -0
- package/dist/esm/chunks/chunk-6OZJCAGZ.js +1 -0
- package/dist/esm/chunks/chunk-A5SYCVQM.js +1 -0
- package/dist/esm/chunks/chunk-BA4LQJJQ.js +1 -0
- package/dist/esm/chunks/chunk-BFTZR4OB.js +4 -0
- package/dist/esm/chunks/chunk-C5JYQCWH.js +1 -0
- package/dist/esm/chunks/chunk-E5YO6274.js +1 -0
- package/dist/esm/chunks/chunk-MPDWM2NN.js +1 -0
- package/dist/esm/chunks/chunk-T44MUL4Y.js +1 -0
- package/dist/esm/chunks/chunk-YLUULATY.js +1 -0
- package/dist/esm/chunks/diagnose-3FEKRIEK.js +14 -0
- package/dist/esm/chunks/extras-6POFBNOP.js +1 -0
- package/dist/esm/chunks/markets-ui-FD6NI2OG.js +1 -0
- package/dist/esm/chunks/search-UJ73YBQ5.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 +320 -365
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +242 -491
- package/dist/tether.account.min.js +1 -1
- package/dist/tether.analytics.js +66 -88
- package/dist/tether.analytics.min.js +1 -1
- package/dist/tether.diagnose.js +36 -33
- package/dist/tether.diagnose.min.js +2 -2
- package/dist/tether.extras.js +28 -36
- package/dist/tether.extras.min.js +1 -1
- package/dist/tether.js +1270 -894
- package/dist/tether.markets.js +73 -90
- 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 +44 -68
- 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/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 +79 -0
- package/dist/types/src/cart/render.d.ts +17 -0
- package/dist/types/src/cart/store.d.ts +182 -0
- package/dist/types/src/cart.d.ts +106 -0
- package/dist/types/src/client.d.ts +100 -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 +185 -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 +88 -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 -23
- package/src/account/addresses-view.ts +58 -0
- package/src/account/answers.ts +79 -0
- package/src/account/markup.ts +36 -0
- package/src/account/operations.ts +145 -0
- package/src/account/{order-lines.js → order-lines.ts} +20 -41
- package/src/account/order-list.ts +107 -0
- package/src/account/order-page.ts +137 -0
- package/src/account/page-answers.ts +317 -0
- package/src/account/{page-events.js → page-events.ts} +55 -90
- package/src/account/server-client.ts +183 -0
- package/src/account/{subscriptions-view.js → subscriptions-view.ts} +25 -68
- package/src/account-api.ts +98 -0
- package/src/account-marks.ts +35 -0
- package/src/{account-pages.js → account-pages.ts} +29 -37
- package/src/account.ts +284 -0
- package/src/amounts.ts +64 -0
- package/src/{analytics-shopify.js → analytics-shopify.ts} +112 -107
- package/src/analytics.ts +315 -0
- package/src/{api-version.js → api-version.ts} +25 -31
- package/src/{astro.js → astro.ts} +110 -104
- package/src/cart/cart-shape.ts +25 -0
- package/src/cart/checkout.ts +101 -0
- package/src/cart/{drawer.js → drawer.ts} +18 -31
- package/src/cart/mutations.ts +237 -0
- package/src/cart/{render.js → render.ts} +68 -96
- package/src/cart/store.ts +279 -0
- package/src/{cart.js → cart.ts} +82 -128
- package/src/client.ts +391 -0
- package/src/{collection.js → collection.ts} +30 -38
- package/src/config-sections.ts +284 -0
- package/src/config.ts +480 -0
- package/src/controls/{cart-controls.js → cart-controls.ts} +22 -54
- package/src/controls/{discount.js → discount.ts} +13 -34
- package/src/controls/{extras.js → extras.ts} +15 -24
- package/src/controls/{navigation.js → navigation.ts} +24 -30
- package/src/{diagnose-markup.js → diagnose-markup.ts} +37 -72
- package/src/{diagnose.js → diagnose.ts} +95 -96
- package/src/dom.ts +268 -0
- package/src/{extras.js → extras.ts} +43 -52
- package/src/fragments.ts +261 -0
- package/src/globals.d.ts +15 -0
- package/src/{images.js → images.ts} +30 -43
- package/src/index.ts +415 -0
- package/src/initial.ts +101 -0
- package/src/lazy-iife.ts +197 -0
- package/src/lazy.ts +42 -0
- package/src/{loader.js → loader.ts} +149 -123
- package/src/market-codes.ts +6 -0
- package/src/{markets-ui.js → markets-ui.ts} +91 -68
- package/src/markets.ts +246 -0
- package/src/markup.ts +127 -0
- package/src/{mounting.js → mounting.ts} +88 -101
- package/src/{page-market.js → page-market.ts} +32 -38
- package/src/part-integrity.ts +7 -0
- package/src/parts/account-pages.ts +7 -0
- package/src/parts/account.ts +7 -0
- package/src/parts/analytics.ts +7 -0
- package/src/parts/diagnose.ts +7 -0
- package/src/parts/extras.ts +7 -0
- package/src/parts/markets.ts +7 -0
- package/src/parts/search.ts +7 -0
- package/src/presets/barba.ts +194 -0
- package/src/{product-events.js → product-events.ts} +33 -48
- package/src/{product-render.js → product-render.ts} +38 -77
- package/src/product-shape.ts +49 -0
- package/src/product-state.ts +318 -0
- package/src/{product.js → product.ts} +90 -126
- package/src/{repeaters.js → repeaters.ts} +40 -52
- package/src/{search.js → search.ts} +85 -103
- package/src/selling-plans.ts +145 -0
- package/src/server.ts +211 -0
- package/src/{shopify-values.js → shopify-values.ts} +24 -22
- package/src/{status.js → status.ts} +34 -54
- package/src/storage.ts +34 -0
- package/src/store-html.ts +359 -0
- package/src/storefront/catalog.ts +196 -0
- package/src/storefront/{json-ld.js → json-ld.ts} +45 -68
- package/src/{variant-render.js → variant-render.ts} +24 -50
- 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-list.js +0 -91
- package/src/account/order-page.js +0 -192
- 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-api.js +0 -118
- package/src/account-links.js +0 -35
- package/src/account.js +0 -342
- package/src/amounts.js +0 -36
- package/src/analytics.js +0 -352
- package/src/cart/checkout.js +0 -97
- package/src/cart/mutations.js +0 -234
- package/src/cart/store.js +0 -326
- package/src/client.js +0 -267
- package/src/config.js +0 -349
- package/src/dom.js +0 -287
- package/src/fragments.js +0 -237
- 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/market-codes.js +0 -35
- package/src/markets.js +0 -334
- package/src/markup.js +0 -64
- 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-state.js +0 -318
- package/src/selling-plans.js +0 -154
- package/src/server.js +0 -215
- package/src/store-html.js +0 -0
- package/src/storefront/catalog.js +0 -199
- 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.search.js
CHANGED
|
@@ -1,34 +1,42 @@
|
|
|
1
|
-
"use strict";
|
|
2
1
|
(() => {
|
|
3
2
|
var __defProp = Object.defineProperty;
|
|
3
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
4
4
|
var __export = (target, all) => {
|
|
5
5
|
for (var name in all)
|
|
6
6
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
7
7
|
};
|
|
8
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
8
9
|
|
|
9
|
-
// src/search.
|
|
10
|
+
// src/search.ts
|
|
10
11
|
var search_exports = {};
|
|
11
12
|
__export(search_exports, {
|
|
12
13
|
mountSearch: () => mountSearch
|
|
13
14
|
});
|
|
14
15
|
|
|
15
|
-
// tether-core:config.
|
|
16
|
-
var m = window.Tether.__core["config.
|
|
17
|
-
var
|
|
16
|
+
// tether-core:config.ts
|
|
17
|
+
var m = window.Tether.__core["config.ts"];
|
|
18
|
+
var [cfg, isConfigured, log, MARKET_SOURCES, parseMarkets, reportError, warn] = m;
|
|
18
19
|
|
|
19
|
-
// tether-core:client.
|
|
20
|
-
var m2 = window.Tether.__core["client.
|
|
21
|
-
var
|
|
20
|
+
// tether-core:client.ts
|
|
21
|
+
var m2 = window.Tether.__core["client.ts"];
|
|
22
|
+
var [endpoint, gql, orNull, shaped, TetherError] = m2;
|
|
22
23
|
|
|
23
|
-
// tether-core:dom.
|
|
24
|
-
var m3 = window.Tether.__core["dom.
|
|
25
|
-
var
|
|
24
|
+
// tether-core:dom.ts
|
|
25
|
+
var m3 = window.Tether.__core["dom.ts"];
|
|
26
|
+
var [ANALYTICS_EVENTS, attr, attributesFromJson, emit, formatDate, formatMoney, get, has, hasTemplate, isOwnEvent, parseJSONAttr, productUrl, qsa, repeat, sel, setImage, setText, show] = m3;
|
|
26
27
|
|
|
27
|
-
// tether-core:mounting.
|
|
28
|
-
var m4 = window.Tether.__core["mounting.
|
|
29
|
-
var
|
|
28
|
+
// tether-core:mounting.ts
|
|
29
|
+
var m4 = window.Tether.__core["mounting.ts"];
|
|
30
|
+
var [liveLists, liveProducts, mountedProducts, mountProduct, pruneLive, within] = m4;
|
|
30
31
|
|
|
31
|
-
// src/search.
|
|
32
|
+
// src/search.ts
|
|
33
|
+
var isSearchAnswer = shaped({
|
|
34
|
+
predictiveSearch: orNull({
|
|
35
|
+
products: [{ priceRange: "object" }],
|
|
36
|
+
collections: ["object"],
|
|
37
|
+
queries: [{ text: "string" }]
|
|
38
|
+
})
|
|
39
|
+
});
|
|
32
40
|
var QUERY = `query TetherPredictive($q:String!,$limit:Int!){predictiveSearch(query:$q,limit:$limit,limitScope:EACH,types:[PRODUCT,COLLECTION,QUERY]){products{id handle title availableForSale featuredImage{url altText}priceRange{minVariantPrice{amount currencyCode}}}collections{handle title image{url altText}}queries{text}}}`;
|
|
33
41
|
var DEFAULT_LIMIT = 6;
|
|
34
42
|
var DEFAULT_MIN_CHARACTERS = 2;
|
|
@@ -51,16 +59,23 @@
|
|
|
51
59
|
function linkOption(node, href) {
|
|
52
60
|
const link = linkIn(node);
|
|
53
61
|
if (!link) return;
|
|
54
|
-
link.setAttribute("href", href);
|
|
62
|
+
if (href) link.setAttribute("href", href);
|
|
55
63
|
link.setAttribute("role", "option");
|
|
56
64
|
}
|
|
57
65
|
var SearchBox = class {
|
|
58
|
-
/**
|
|
59
|
-
* @param {HTMLElement} root
|
|
60
|
-
* @param {HTMLInputElement} input
|
|
61
|
-
* @param {HTMLElement} results
|
|
62
|
-
*/
|
|
63
66
|
constructor(root, input, results) {
|
|
67
|
+
__publicField(this, "root");
|
|
68
|
+
__publicField(this, "input");
|
|
69
|
+
__publicField(this, "results");
|
|
70
|
+
__publicField(this, "limit");
|
|
71
|
+
__publicField(this, "minCharacters");
|
|
72
|
+
__publicField(this, "resultsId");
|
|
73
|
+
/** Counts searches, so only the latest one renders. */
|
|
74
|
+
__publicField(this, "searchNumber", 0);
|
|
75
|
+
__publicField(this, "timer");
|
|
76
|
+
__publicField(this, "lastTerm", "");
|
|
77
|
+
/** Results per term, for typing back and forth. */
|
|
78
|
+
__publicField(this, "cache", /* @__PURE__ */ new Map());
|
|
64
79
|
this.root = root;
|
|
65
80
|
this.input = input;
|
|
66
81
|
this.results = results;
|
|
@@ -71,10 +86,6 @@
|
|
|
71
86
|
results.id = `${cfg.prefix}-search-${searchBoxCount}`;
|
|
72
87
|
}
|
|
73
88
|
this.resultsId = results.id;
|
|
74
|
-
this.searchNumber = 0;
|
|
75
|
-
this.timer = void 0;
|
|
76
|
-
this.lastTerm = "";
|
|
77
|
-
this.cache = /* @__PURE__ */ new Map();
|
|
78
89
|
this.setUpAria();
|
|
79
90
|
this.listen();
|
|
80
91
|
}
|
|
@@ -102,17 +113,12 @@
|
|
|
102
113
|
this.onKeydown(event);
|
|
103
114
|
});
|
|
104
115
|
document.addEventListener("click", (event) => {
|
|
105
|
-
if (!this.root.contains(
|
|
106
|
-
/** @type {Node | null} */
|
|
107
|
-
event.target
|
|
108
|
-
)) this.open(false);
|
|
116
|
+
if (!this.root.contains(event.target)) this.open(false);
|
|
109
117
|
});
|
|
110
118
|
}
|
|
111
|
-
/** @param {SearchState} state */
|
|
112
119
|
setState(state) {
|
|
113
120
|
this.root.setAttribute(attr("search-state"), state);
|
|
114
121
|
}
|
|
115
|
-
/** @param {boolean} open */
|
|
116
122
|
open(open) {
|
|
117
123
|
this.root.classList.toggle("is-open", open);
|
|
118
124
|
this.results.hidden = !open;
|
|
@@ -122,18 +128,13 @@
|
|
|
122
128
|
options() {
|
|
123
129
|
return qsa(this.results, '[role="option"]');
|
|
124
130
|
}
|
|
125
|
-
/**
|
|
126
|
-
* @param {string} term
|
|
127
|
-
* @returns {Promise<SearchResults | null>}
|
|
128
|
-
*/
|
|
129
131
|
async fetchResults(term) {
|
|
130
132
|
const cached = this.cache.get(term);
|
|
131
133
|
if (cached) return cached;
|
|
132
|
-
const data = await gql(QUERY, { q: term, limit: this.limit });
|
|
134
|
+
const data = await gql(QUERY, { q: term, limit: this.limit }, isSearchAnswer);
|
|
133
135
|
this.cache.set(term, data.predictiveSearch);
|
|
134
136
|
return data.predictiveSearch;
|
|
135
137
|
}
|
|
136
|
-
/** @param {string} term */
|
|
137
138
|
async search(term) {
|
|
138
139
|
this.searchNumber += 1;
|
|
139
140
|
const searchNumber = this.searchNumber;
|
|
@@ -155,28 +156,18 @@
|
|
|
155
156
|
reportError(error, "search");
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
|
-
/**
|
|
159
|
-
* @template Item
|
|
160
|
-
* @param {string} name The list attribute, e.g. `search-products`.
|
|
161
|
-
* @param {readonly Item[]} items
|
|
162
|
-
* @param {(node: HTMLElement, item: Item) => void} render
|
|
163
|
-
*/
|
|
159
|
+
/** @param name The list attribute, e.g. `search-products`. */
|
|
164
160
|
fillList(name, items, render) {
|
|
165
161
|
qsa(this.results, sel(name)).forEach((list) => {
|
|
166
162
|
repeat(list, items, render);
|
|
167
163
|
show(list, items.length > 0);
|
|
168
164
|
});
|
|
169
165
|
}
|
|
170
|
-
/** @param {string} text */
|
|
171
166
|
queryUrl(text) {
|
|
172
167
|
const action = this.root.getAttribute("action") || DEFAULT_ACTION;
|
|
173
168
|
const separator = action.includes("?") ? "&" : "?";
|
|
174
169
|
return `${action}${separator}${this.input.name || "q"}=${encodeURIComponent(text)}`;
|
|
175
170
|
}
|
|
176
|
-
/**
|
|
177
|
-
* @param {SearchResults | null} results
|
|
178
|
-
* @param {string} term
|
|
179
|
-
*/
|
|
180
171
|
render(results, term) {
|
|
181
172
|
const products = results?.products ?? [];
|
|
182
173
|
const collections = results?.collections ?? [];
|
|
@@ -201,7 +192,6 @@
|
|
|
201
192
|
this.open(true);
|
|
202
193
|
emit("search", { term, products, collections, queries }, this.root);
|
|
203
194
|
}
|
|
204
|
-
/** @param {number} count */
|
|
205
195
|
showCount(count) {
|
|
206
196
|
qsa(this.root, sel("show")).forEach((el) => {
|
|
207
197
|
const condition = get(el, "show");
|
|
@@ -213,10 +203,6 @@
|
|
|
213
203
|
option.setAttribute("aria-selected", "false");
|
|
214
204
|
});
|
|
215
205
|
}
|
|
216
|
-
/**
|
|
217
|
-
* @param {HTMLElement[]} options
|
|
218
|
-
* @param {number} index
|
|
219
|
-
*/
|
|
220
206
|
highlight(options, index) {
|
|
221
207
|
options.forEach((option2, optionIndex) => {
|
|
222
208
|
option2.setAttribute("aria-selected", String(optionIndex === index));
|
|
@@ -226,7 +212,6 @@
|
|
|
226
212
|
this.input.setAttribute("aria-activedescendant", option.id);
|
|
227
213
|
option.scrollIntoView?.({ block: "nearest" });
|
|
228
214
|
}
|
|
229
|
-
/** @param {KeyboardEvent} event */
|
|
230
215
|
onKeydown(event) {
|
|
231
216
|
const options = this.options();
|
|
232
217
|
const current = options.findIndex((option) => option.getAttribute("aria-selected") === "true");
|
|
@@ -249,23 +234,14 @@
|
|
|
249
234
|
for (const el of within(root, sel("search"))) {
|
|
250
235
|
if (mounted.has(el)) continue;
|
|
251
236
|
mounted.add(el);
|
|
252
|
-
const input = (
|
|
253
|
-
|
|
254
|
-
el.querySelector(sel("search-input")) ?? el.querySelector("input")
|
|
255
|
-
);
|
|
256
|
-
const results = (
|
|
257
|
-
/** @type {HTMLElement | null} */
|
|
258
|
-
el.querySelector(sel("search-results"))
|
|
259
|
-
);
|
|
237
|
+
const input = el.querySelector(sel("search-input")) ?? el.querySelector("input");
|
|
238
|
+
const results = el.querySelector(sel("search-results"));
|
|
260
239
|
if (input && results) new SearchBox(el, input, results);
|
|
261
240
|
}
|
|
262
241
|
}
|
|
263
242
|
|
|
264
|
-
// src/parts/search.
|
|
265
|
-
var { Tether } =
|
|
266
|
-
/** @type {Window & { Tether?: { __parts: Record<string, unknown> } }} */
|
|
267
|
-
window
|
|
268
|
-
);
|
|
243
|
+
// src/parts/search.ts
|
|
244
|
+
var { Tether } = window;
|
|
269
245
|
if (!Tether) throw new Error("tether.search.js runs after the Tether script, which loads it");
|
|
270
|
-
Tether.__parts
|
|
246
|
+
Tether.__parts.search = search_exports;
|
|
271
247
|
})();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
(()=>{var T=Object.defineProperty;var D=(i,t,e)=>t in i?T(i,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):i[t]=e;var V=(i,t)=>{for(var e in t)T(i,e,{get:t[e],enumerable:!0})};var a=(i,t,e)=>D(i,typeof t!="symbol"?t+"":t,e);var g={};V(g,{mountSearch:()=>W});var O=window.Tether.__core["config.ts"],[u,X,b,Z,tt,y,et]=O;var F=window.Tether.__core["client.ts"],[it,E,w,A,st]=F;var K=window.Tether.__core["dom.ts"],[at,I,ot,L,lt,x,m,ct,ht,ut,mt,C,h,q,l,M,v,d]=K;var Q=window.Tether.__core["mounting.ts"],[pt,ft,gt,St,Tt,R]=Q;var j=A({predictiveSearch:w({products:[{priceRange:"object"}],collections:["object"],queries:[{text:"string"}]})}),Y="query TetherPredictive($q:String!,$limit:Int!){predictiveSearch(query:$q,limit:$limit,limitScope:EACH,types:[PRODUCT,COLLECTION,QUERY]){products{id handle title availableForSale featuredImage{url altText}priceRange{minVariantPrice{amount currencyCode}}}collections{handle title image{url altText}}queries{text}}}",B=6,J=2,z="/search",N=new WeakSet,P=0;function _(i,t,e){return parseInt(m(i,t)||"",10)||e}var U=i=>i.tagName==="A"?i:i.querySelector("a");function p(i,t){h(i,l("search-bind")).forEach(e=>{let r=m(e,"search-bind");r==="image"?M(e,t.image?.url,t.image?.altText||t.title):(r==="title"||r==="price"||r==="text")&&t[r]!=null&&v(e,t[r])})}function k(i,t){let e=U(i);e&&(t&&e.setAttribute("href",t),e.setAttribute("role","option"))}var f=class{constructor(t,e,r){a(this,"root");a(this,"input");a(this,"results");a(this,"limit");a(this,"minCharacters");a(this,"resultsId");a(this,"searchNumber",0);a(this,"timer");a(this,"lastTerm","");a(this,"cache",new Map);this.root=t,this.input=e,this.results=r,this.limit=_(t,"search-limit",B),this.minCharacters=_(t,"search-min",J),r.id||(P+=1,r.id=`${u.prefix}-search-${P}`),this.resultsId=r.id,this.setUpAria(),this.listen()}setUpAria(){this.input.setAttribute("role","combobox"),this.input.setAttribute("aria-autocomplete","list"),this.input.setAttribute("aria-controls",this.resultsId),this.input.setAttribute("aria-expanded","false"),this.input.setAttribute("autocomplete","off"),this.results.hasAttribute("role")||this.results.setAttribute("role","listbox"),this.results.hidden=!0}listen(){this.input.addEventListener("input",()=>{let t=this.input.value.trim();t!==this.lastTerm&&(this.lastTerm=t,clearTimeout(this.timer),this.timer=setTimeout(()=>void this.search(t),u.searchDelay))}),this.input.addEventListener("focus",()=>{this.input.value.trim().length>=this.minCharacters&&this.options().length&&this.open(!0)}),this.input.addEventListener("keydown",t=>{this.onKeydown(t)}),document.addEventListener("click",t=>{this.root.contains(t.target)||this.open(!1)})}setState(t){this.root.setAttribute(I("search-state"),t)}open(t){this.root.classList.toggle("is-open",t),this.results.hidden=!t,this.input.setAttribute("aria-expanded",String(t)),t||this.input.removeAttribute("aria-activedescendant")}options(){return h(this.results,'[role="option"]')}async fetchResults(t){let e=this.cache.get(t);if(e)return e;let r=await E(Y,{q:t,limit:this.limit},j);return this.cache.set(t,r.predictiveSearch),r.predictiveSearch}async search(t){this.searchNumber+=1;let e=this.searchNumber;if(t.length<this.minCharacters){this.open(!1),this.setState("idle");return}this.setState("loading");try{let r=await this.fetchResults(t);e===this.searchNumber&&this.render(r,t)}catch(r){if(e!==this.searchNumber){b("a superseded search failed",r);return}this.setState("error"),y(r,"search")}}fillList(t,e,r){h(this.results,l(t)).forEach(o=>{q(o,e,r),d(o,e.length>0)})}queryUrl(t){let e=this.root.getAttribute("action")||z,r=e.includes("?")?"&":"?";return`${e}${r}${this.input.name||"q"}=${encodeURIComponent(t)}`}render(t,e){let r=t?.products??[],o=t?.collections??[],c=(t?.queries??[]).filter(n=>n.text.toLowerCase()!==e.toLowerCase());this.fillList("search-products",r,(n,s)=>{let H=x(s.priceRange.minVariantPrice);p(n,{title:s.title,price:H,image:s.featuredImage}),k(n,C(s.handle,s.id)),n.classList.toggle("is-sold-out",!s.availableForSale)}),this.fillList("search-collections",o,(n,s)=>{p(n,{title:s.title,image:s.image}),k(n,`${(u.collectionPath||"/collection").replace(/\/$/,"")}/${s.handle}`)}),this.fillList("search-queries",c,(n,s)=>{p(n,{text:s.text}),U(n)?.setAttribute("href",this.queryUrl(s.text))});let S=r.length+o.length+c.length;this.showCount(S),this.setState(S?"ready":"empty"),this.open(!0),L("search",{term:e,products:r,collections:o,queries:c},this.root)}showCount(t){h(this.root,l("show")).forEach(e=>{let r=m(e,"show");r==="search-empty"&&d(e,t===0),r==="search-results"&&d(e,t>0)}),this.options().forEach((e,r)=>{e.id||(e.id=`${this.resultsId}-${r}`),e.setAttribute("aria-selected","false")})}highlight(t,e){t.forEach((o,c)=>{o.setAttribute("aria-selected",String(c===e))});let r=t[e];r&&(this.input.setAttribute("aria-activedescendant",r.id),r.scrollIntoView?.({block:"nearest"}))}onKeydown(t){let e=this.options(),r=e.findIndex(o=>o.getAttribute("aria-selected")==="true");t.key==="ArrowDown"&&e.length?(t.preventDefault(),this.open(!0),this.highlight(e,(r+1)%e.length)):t.key==="ArrowUp"&&e.length?(t.preventDefault(),this.highlight(e,r<=0?e.length-1:r-1)):t.key==="Enter"&&r>=0?(t.preventDefault(),e[r]?.click()):t.key==="Escape"&&this.open(!1)}};function W(i=document){for(let t of R(i,l("search"))){if(N.has(t))continue;N.add(t);let e=t.querySelector(l("search-input"))??t.querySelector("input"),r=t.querySelector(l("search-results"));e&&r&&new f(t,e,r)}}var{Tether:$}=window;if(!$)throw new Error("tether.search.js runs after the Tether script, which loads it");$.__parts.search=g;})();
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/** A `MoneyV2`. */
|
|
2
|
+
interface Money {
|
|
3
|
+
amount: string;
|
|
4
|
+
currencyCode: string;
|
|
5
|
+
}
|
|
6
|
+
export interface AccountImage {
|
|
7
|
+
url: string;
|
|
8
|
+
altText: string | null;
|
|
9
|
+
}
|
|
10
|
+
/** A line of an order. */
|
|
11
|
+
export interface AccountOrderLine {
|
|
12
|
+
title: string | null;
|
|
13
|
+
variantTitle: string | null;
|
|
14
|
+
quantity: number;
|
|
15
|
+
image: AccountImage | null;
|
|
16
|
+
}
|
|
17
|
+
/** An order in the customer's list. */
|
|
18
|
+
export interface AccountOrder {
|
|
19
|
+
id: string;
|
|
20
|
+
name: string | null;
|
|
21
|
+
processedAt: string | null;
|
|
22
|
+
financialStatus: string | null;
|
|
23
|
+
fulfillmentStatus: string | null;
|
|
24
|
+
statusPageUrl: string | null;
|
|
25
|
+
totalPrice: Money | null;
|
|
26
|
+
lineItems: {
|
|
27
|
+
nodes: AccountOrderLine[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** `missing-id` when an order has no id, which each side reports in its own error type. */
|
|
31
|
+
type OrdersReading = {
|
|
32
|
+
kind: 'read';
|
|
33
|
+
orders: AccountOrder[];
|
|
34
|
+
} | {
|
|
35
|
+
kind: 'missing-id';
|
|
36
|
+
};
|
|
37
|
+
export declare function moneyOrNull(value: unknown): Money | null;
|
|
38
|
+
export declare function imageOrNull(value: unknown): AccountImage | null;
|
|
39
|
+
export declare const toOrderLine: (value: unknown) => AccountOrderLine;
|
|
40
|
+
/**
|
|
41
|
+
* @param value A list of orders: Shopify's `orders.nodes`, or the `orders` of the Worker's `/orders`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function readOrders(value: unknown): OrdersReading;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Worker's routes for the customer account operations, the contract between the browser's account part and the
|
|
3
|
+
* Worker: what the browser sends for each operation, and which method and path it has under `/_tether/account`. The
|
|
4
|
+
* operations themselves (their documents and input checks) run only in the Worker, in `account/operations.ts`.
|
|
5
|
+
*/
|
|
6
|
+
export type SubscriptionAction = 'pause' | 'resume' | 'skip' | 'unskip' | 'cancel';
|
|
7
|
+
/** Shopify's `CustomerAddressInput` fields; the Worker keeps only these, with bounded lengths. */
|
|
8
|
+
export interface AddressFields {
|
|
9
|
+
firstName?: string;
|
|
10
|
+
lastName?: string;
|
|
11
|
+
company?: string;
|
|
12
|
+
address1?: string;
|
|
13
|
+
address2?: string;
|
|
14
|
+
city?: string;
|
|
15
|
+
zip?: string;
|
|
16
|
+
zoneCode?: string;
|
|
17
|
+
/** Two letters. */
|
|
18
|
+
countryCode?: string;
|
|
19
|
+
phoneNumber?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ReturnLine {
|
|
22
|
+
lineItemId: string;
|
|
23
|
+
quantity: number;
|
|
24
|
+
reasonId?: string;
|
|
25
|
+
note?: string;
|
|
26
|
+
}
|
|
27
|
+
/** What the browser sends for each operation. Ids are gids, or the number at their end. */
|
|
28
|
+
export interface OperationInputs {
|
|
29
|
+
order: {
|
|
30
|
+
id: string | number;
|
|
31
|
+
};
|
|
32
|
+
addresses: Record<string, never>;
|
|
33
|
+
subscriptions: Record<string, never>;
|
|
34
|
+
profile: {
|
|
35
|
+
firstName?: string | undefined;
|
|
36
|
+
lastName?: string | undefined;
|
|
37
|
+
};
|
|
38
|
+
/** Without `id`, a new address; without fields, only makes the address the default. */
|
|
39
|
+
saveAddress: {
|
|
40
|
+
id?: string | undefined;
|
|
41
|
+
address: AddressFields | null;
|
|
42
|
+
isDefault?: boolean | undefined;
|
|
43
|
+
};
|
|
44
|
+
deleteAddress: {
|
|
45
|
+
id: string;
|
|
46
|
+
};
|
|
47
|
+
subscription: {
|
|
48
|
+
id: string | number;
|
|
49
|
+
action: SubscriptionAction;
|
|
50
|
+
};
|
|
51
|
+
requestReturn: {
|
|
52
|
+
orderId: string | number;
|
|
53
|
+
lines: ReturnLine[];
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export type OperationName = keyof OperationInputs;
|
|
57
|
+
/**
|
|
58
|
+
* Each operation's input fields as they reach the Worker: from a browser, a page script or anyone else, so each value
|
|
59
|
+
* is unchecked until the operation checks it.
|
|
60
|
+
*/
|
|
61
|
+
export type UncheckedInputs = {
|
|
62
|
+
[Name in OperationName]: {
|
|
63
|
+
readonly [Field in keyof OperationInputs[Name]]?: unknown;
|
|
64
|
+
};
|
|
65
|
+
};
|
|
66
|
+
/** One operation a Worker request asks for, with the fields its route reads, each one unchecked. */
|
|
67
|
+
export interface OperationCall {
|
|
68
|
+
name: OperationName;
|
|
69
|
+
input: Readonly<Record<string, unknown>>;
|
|
70
|
+
}
|
|
71
|
+
export declare const OPERATION_ROUTES: {
|
|
72
|
+
order: {
|
|
73
|
+
name: "order";
|
|
74
|
+
method: "GET";
|
|
75
|
+
path: string;
|
|
76
|
+
fields: {
|
|
77
|
+
id: true;
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
addresses: {
|
|
81
|
+
name: "addresses";
|
|
82
|
+
method: "GET";
|
|
83
|
+
path: string;
|
|
84
|
+
fields: {};
|
|
85
|
+
};
|
|
86
|
+
subscriptions: {
|
|
87
|
+
name: "subscriptions";
|
|
88
|
+
method: "GET";
|
|
89
|
+
path: string;
|
|
90
|
+
fields: {};
|
|
91
|
+
};
|
|
92
|
+
profile: {
|
|
93
|
+
name: "profile";
|
|
94
|
+
method: "POST";
|
|
95
|
+
path: string;
|
|
96
|
+
fields: {
|
|
97
|
+
firstName: true;
|
|
98
|
+
lastName: true;
|
|
99
|
+
};
|
|
100
|
+
};
|
|
101
|
+
saveAddress: {
|
|
102
|
+
name: "saveAddress";
|
|
103
|
+
method: "POST";
|
|
104
|
+
path: string;
|
|
105
|
+
fields: {
|
|
106
|
+
id: true;
|
|
107
|
+
address: true;
|
|
108
|
+
isDefault: true;
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
deleteAddress: {
|
|
112
|
+
name: "deleteAddress";
|
|
113
|
+
method: "POST";
|
|
114
|
+
path: string;
|
|
115
|
+
fields: {
|
|
116
|
+
id: true;
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
subscription: {
|
|
120
|
+
name: "subscription";
|
|
121
|
+
method: "POST";
|
|
122
|
+
path: string;
|
|
123
|
+
fields: {
|
|
124
|
+
id: true;
|
|
125
|
+
action: true;
|
|
126
|
+
};
|
|
127
|
+
};
|
|
128
|
+
requestReturn: {
|
|
129
|
+
name: "requestReturn";
|
|
130
|
+
method: "POST";
|
|
131
|
+
path: string;
|
|
132
|
+
fields: {
|
|
133
|
+
orderId: true;
|
|
134
|
+
lines: true;
|
|
135
|
+
};
|
|
136
|
+
};
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* The operation a Worker request asks for, with its input read from the query string or the body.
|
|
140
|
+
*
|
|
141
|
+
* @param path The path under `/_tether/account`.
|
|
142
|
+
* @param body The JSON body of a POST; `{}` for a GET.
|
|
143
|
+
* @returns `null` when no operation has this method and path.
|
|
144
|
+
*/
|
|
145
|
+
export declare function operationCall(method: string, path: string, url: URL, body: Record<string, unknown>): OperationCall | null;
|
|
146
|
+
/**
|
|
147
|
+
* The method and path (with its query string, for a GET) the browser requests an operation with. A POST sends the
|
|
148
|
+
* input as its JSON body.
|
|
149
|
+
*/
|
|
150
|
+
export declare function operationRequest<Name extends OperationName>(name: Name, input: OperationInputs[Name]): {
|
|
151
|
+
method: 'GET' | 'POST';
|
|
152
|
+
path: string;
|
|
153
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Shopify API versions Tether is built and smoke-tested against, for the browser script and the Worker alike. The
|
|
3
|
+
* three APIs are versioned separately, so each has its own constant even while they hold the same quarter. A new
|
|
4
|
+
* version must pass `npm run smoke` on both the pinned version and the next one.
|
|
5
|
+
*/
|
|
6
|
+
/** A dated, quarterly version such as `2026-07`. */
|
|
7
|
+
export type ApiVersion = `${number}-${'01' | '04' | '07' | '10'}`;
|
|
8
|
+
/** The Storefront API: products, carts, cart buyer identity. */
|
|
9
|
+
export declare const STOREFRONT_API_VERSION: ApiVersion;
|
|
10
|
+
/** The Customer Account API: the logged-in customer's orders, addresses and subscriptions. */
|
|
11
|
+
export declare const CUSTOMER_ACCOUNT_API_VERSION: ApiVersion;
|
|
12
|
+
/** The Admin API the sync reads products with. */
|
|
13
|
+
export declare const ADMIN_API_VERSION: ApiVersion;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading JSON that arrives from outside (Shopify's answers, a Worker request body) field by field, for the browser
|
|
3
|
+
* script and the Worker alike. Every reader takes `unknown`, so a missing or mistyped field is `undefined`, never a
|
|
4
|
+
* crash.
|
|
5
|
+
*/
|
|
6
|
+
/** A plain object, as parsed JSON holds it. */
|
|
7
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
8
|
+
/** The field `key` of `value`, or `undefined` when `value` is not a plain object. */
|
|
9
|
+
export declare function field(value: unknown, key: string): unknown;
|
|
10
|
+
/** The value when it is an array, else an empty list. */
|
|
11
|
+
export declare function asList(value: unknown): unknown[];
|
|
12
|
+
/** A non-empty string, or `null`: Shopify sends `""` and `null` alike for an unset text field. */
|
|
13
|
+
export declare function textOrNull(value: unknown): string | null;
|
|
14
|
+
export declare const numberOrNull: (value: unknown) => number | null;
|
|
15
|
+
/** A count Shopify always sends; 0 for an answer without it. */
|
|
16
|
+
export declare const countOrZero: (value: unknown) => number;
|
|
17
|
+
export declare const listOf: <T>(value: unknown, read: (item: unknown) => T) => T[];
|
|
18
|
+
/**
|
|
19
|
+
* @param connection A GraphQL connection: `{ nodes: [...] }`.
|
|
20
|
+
*/
|
|
21
|
+
export declare const nodesOf: <T>(connection: unknown, read: (item: unknown) => T) => {
|
|
22
|
+
nodes: T[];
|
|
23
|
+
};
|
|
24
|
+
/** The non-empty strings of a list. */
|
|
25
|
+
export declare const textList: (value: unknown) => string[];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Country and language codes as Shopify takes them, parsed the same way in the browser and the Worker: anything that
|
|
3
|
+
* isn't a code is dropped rather than repaired.
|
|
4
|
+
*/
|
|
5
|
+
/** An ISO 3166-1 alpha-2 code, upper case. */
|
|
6
|
+
export type CountryCode = string & {
|
|
7
|
+
readonly __brand: 'CountryCode';
|
|
8
|
+
};
|
|
9
|
+
/** A Shopify `LanguageCode` value. */
|
|
10
|
+
export type LanguageCode = string & {
|
|
11
|
+
readonly __brand: 'LanguageCode';
|
|
12
|
+
};
|
|
13
|
+
/** @returns `'de'` → `'DE'`; `null` for anything that is not a country code. */
|
|
14
|
+
export declare function toCountry(value: unknown): CountryCode | null;
|
|
15
|
+
/** @returns `'de-DE'` → `'DE'`, `'pt-BR'` → `'PT_BR'`, `'en'` → `'EN'`; `null` otherwise. */
|
|
16
|
+
export declare function toLanguage(value: unknown): LanguageCode | null;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { CustomerAddress } from './page-answers.js';
|
|
2
|
+
/** @returns The address with this id in the list shown last. */
|
|
3
|
+
export declare const shownAddress: (id: string) => CustomerAddress | undefined;
|
|
4
|
+
/** Loads and renders the addresses. A failure is reported and shows `addresses-error`, never "no addresses yet". */
|
|
5
|
+
export declare function renderAddresses(root: ParentNode): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Worker's account answers as typed values. The Worker passes on what Shopify answers to its fixed documents, and
|
|
3
|
+
* each answer is read once, field by field. Every field a document selects is kept. One that is missing or of another
|
|
4
|
+
* type reads as `null` (a list as empty, a count as 0): a page with one blank field is better than no page. The ids
|
|
5
|
+
* the pages act on are required, and an answer without one is an error. This module reads what the account part
|
|
6
|
+
* shows (the customer, their orders through the reader the Worker shares, Shopify's `userErrors`); page-answers.ts
|
|
7
|
+
* reads the account pages' answers.
|
|
8
|
+
*/
|
|
9
|
+
import { TetherError } from '../client.js';
|
|
10
|
+
import type { UserError } from '../client.js';
|
|
11
|
+
import type { AccountOrder } from '../../server/src/shared/account-orders.js';
|
|
12
|
+
/** The logged-in customer. */
|
|
13
|
+
export interface AccountCustomer {
|
|
14
|
+
/** The customer's gid; left out when the Worker's `/me` doesn't send it. */
|
|
15
|
+
id?: string;
|
|
16
|
+
firstName: string | null;
|
|
17
|
+
lastName: string | null;
|
|
18
|
+
displayName: string | null;
|
|
19
|
+
emailAddress: {
|
|
20
|
+
emailAddress: string;
|
|
21
|
+
} | null;
|
|
22
|
+
defaultAddress: {
|
|
23
|
+
formatted: string[];
|
|
24
|
+
} | null;
|
|
25
|
+
}
|
|
26
|
+
export declare const unexpected: (what: string) => TetherError;
|
|
27
|
+
/**
|
|
28
|
+
* @param what Named in the error.
|
|
29
|
+
* @throws {TetherError} Without an id.
|
|
30
|
+
*/
|
|
31
|
+
export declare function requiredId(value: unknown, what: string): string;
|
|
32
|
+
/** @param value The `customer` of the Worker's `/me`, which flattens the email and the default address. */
|
|
33
|
+
export declare function toAccountCustomer(value: unknown): AccountCustomer;
|
|
34
|
+
/**
|
|
35
|
+
* @param value The `orders` of the Worker's `/orders`.
|
|
36
|
+
* @throws {TetherError} For an order without an id.
|
|
37
|
+
*/
|
|
38
|
+
export declare function toAccountOrders(value: unknown): AccountOrder[];
|
|
39
|
+
/** @param value Shopify's `userErrors`, as Shopify or the Worker sends them. */
|
|
40
|
+
export declare const toUserErrors: (value: unknown) => UserError[];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shows or hides each `name` element whose value is one of `conditions`' keys; other values are left alone.
|
|
3
|
+
*
|
|
4
|
+
* @param name `show`, `order-show`, `address-show`…
|
|
5
|
+
*/
|
|
6
|
+
export declare function showIn(root: ParentNode, name: string, conditions: Readonly<Record<string, boolean>>): void;
|
|
7
|
+
/**
|
|
8
|
+
* @param row A copy of a repeater's template.
|
|
9
|
+
* @returns The `name` elements in `row`, and `row` itself when it carries `name`.
|
|
10
|
+
*/
|
|
11
|
+
export declare const bindsIn: (row: HTMLElement, name: string) => HTMLElement[];
|
|
12
|
+
/** @returns A Shopify enum as text: `PARTIALLY_FULFILLED` → `partially fulfilled`. */
|
|
13
|
+
export declare const enumText: (value: string | null | undefined) => string;
|
|
14
|
+
/** @returns Shopify's formatted address on one line. */
|
|
15
|
+
export declare const addressText: (address: {
|
|
16
|
+
formatted?: string[] | null;
|
|
17
|
+
} | null | undefined) => string;
|
|
18
|
+
/** A bind's value, looked up by the attribute's text: only the table's own keys, never `constructor` and the like. */
|
|
19
|
+
export declare const ownValue: <T>(values: Readonly<Record<string, T>>, key: string) => T | undefined;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { AddressFields, ReturnLine, SubscriptionAction } from '../../server/src/shared/account-routes.js';
|
|
2
|
+
export type { AddressFields, ReturnLine };
|
|
3
|
+
import type { AccountAddress, CustomerAddress, OpenedReturn, OrderDetail, Profile, SubscriptionChange, SubscriptionContract } from './page-answers.js';
|
|
4
|
+
import type { ServerAnswer, ServerRequest } from './server-client.js';
|
|
5
|
+
import type { AccountCustomer } from './answers.js';
|
|
6
|
+
/** The account part's session, shared so both parts act as one customer. */
|
|
7
|
+
interface PagesContext {
|
|
8
|
+
serverFetch: (path: string, request?: ServerRequest) => Promise<ServerAnswer>;
|
|
9
|
+
customer: () => Promise<AccountCustomer | null>;
|
|
10
|
+
bindCustomer: (root: ParentNode) => Promise<void>;
|
|
11
|
+
/** Forgets the cached customer after a change of their data. */
|
|
12
|
+
reset: () => void;
|
|
13
|
+
}
|
|
14
|
+
/** Called by the account part once, when it loads this part. */
|
|
15
|
+
export declare function setup(shared: PagesContext): void;
|
|
16
|
+
/** @throws {Error} Before `setup`: this part is only ever loaded by the account part, which calls it first. */
|
|
17
|
+
export declare function pagesContext(): PagesContext;
|
|
18
|
+
/**
|
|
19
|
+
* One of the customer's orders, in full (lines, totals, addresses, fulfillments, returns).
|
|
20
|
+
*
|
|
21
|
+
* @param id A gid or the numeric order id.
|
|
22
|
+
* @returns `null` when the customer has no such order.
|
|
23
|
+
*/
|
|
24
|
+
export declare function order(id: string | number): Promise<OrderDetail | null>;
|
|
25
|
+
export declare function updateProfile({ firstName, lastName, }?: {
|
|
26
|
+
firstName?: string;
|
|
27
|
+
lastName?: string;
|
|
28
|
+
}): Promise<Profile>;
|
|
29
|
+
/** @returns The customer's addresses, the default one marked `isDefault`. */
|
|
30
|
+
export declare function addresses(): Promise<CustomerAddress[]>;
|
|
31
|
+
/** Creates an address, or updates the one with `id`; without fields, only makes it the default. */
|
|
32
|
+
export declare function saveAddress(address: AddressFields | null, { id, isDefault }?: {
|
|
33
|
+
id?: string | undefined;
|
|
34
|
+
isDefault?: boolean;
|
|
35
|
+
}): Promise<AccountAddress>;
|
|
36
|
+
/** @returns The deleted address's id. */
|
|
37
|
+
export declare function deleteAddress(id: string): Promise<string>;
|
|
38
|
+
export declare function subscriptions(): Promise<SubscriptionContract[]>;
|
|
39
|
+
/** @returns The changed contract or billing cycle. */
|
|
40
|
+
export declare function subscriptionAction(id: string | number, action: SubscriptionAction): Promise<SubscriptionChange>;
|
|
41
|
+
export declare function requestReturn(orderId: string | number, lines: ReturnLine[]): Promise<OpenedReturn>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { AccountOrderLine } from '../../server/src/shared/account-orders.js';
|
|
2
|
+
import type { OrderDetailLine } from './page-answers.js';
|
|
3
|
+
/** A line of the order list, or of the order page, which has the fields after `image` too. */
|
|
4
|
+
type OrderLine = AccountOrderLine & Partial<Pick<OrderDetailLine, 'id' | 'name' | 'sku' | 'price' | 'totalPrice' | 'suggestedReturnReasonDefinitions'>>;
|
|
5
|
+
/** Line id → how many of it can still be returned. */
|
|
6
|
+
type Returnable = ReadonlyMap<string, number>;
|
|
7
|
+
/**
|
|
8
|
+
* Renders `lines` into a `tether-order-lines` repeater.
|
|
9
|
+
*
|
|
10
|
+
* @param returns `null` for the order list.
|
|
11
|
+
*/
|
|
12
|
+
export declare function fillLines(box: HTMLElement, lines: readonly OrderLine[], returns: Returnable | null): void;
|
|
13
|
+
export {};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { AccountOrder } from '../../server/src/shared/account-orders.js';
|
|
2
|
+
/** How many orders `orders()` answers, and a `tether-orders` list shows, without being told. */
|
|
3
|
+
export declare const DEFAULT_ORDER_COUNT = 10;
|
|
4
|
+
/** The most orders one request asks for: the Worker answers no more. */
|
|
5
|
+
export declare const MAX_ORDER_COUNT = 50;
|
|
6
|
+
/** @returns True for a number of orders one request can ask for: a whole number from 1 to `MAX_ORDER_COUNT`. */
|
|
7
|
+
export declare const isOrderCount: (value: unknown) => value is number;
|
|
8
|
+
/**
|
|
9
|
+
* Fills every `tether-orders` list in `root`. A failure is reported and leaves the lists empty, without claiming the
|
|
10
|
+
* customer has no orders.
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderOrderList(root: ParentNode, loadOrders: (first: number) => Promise<AccountOrder[]>): Promise<void>;
|