@occluze/tether 0.7.0 → 0.7.2
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 +51 -109
- package/dist/esm/astro.js +1 -1
- package/dist/esm/chunks/{account-UIPWJQ3Y.js → account-W4FBRKXA.js} +1 -1
- package/dist/esm/chunks/{account-pages-6ROA6Y4V.js → account-pages-IM3OQTOP.js} +1 -1
- package/dist/esm/chunks/analytics-QDVOLZY3.js +1 -0
- package/dist/esm/chunks/{chunk-T44MUL4Y.js → chunk-AUVAN3KZ.js} +1 -1
- package/dist/esm/chunks/chunk-BMDYNMRC.js +1 -0
- package/dist/esm/chunks/chunk-CUL66KRA.js +1 -0
- package/dist/esm/chunks/{chunk-6OZJCAGZ.js → chunk-CVNRODH3.js} +1 -1
- package/dist/esm/chunks/{chunk-5Z5O3767.js → chunk-CXHPBT7N.js} +1 -1
- package/dist/esm/chunks/chunk-GF6JTI65.js +1 -0
- package/dist/esm/chunks/chunk-MRSDJPBC.js +1 -0
- package/dist/esm/chunks/chunk-NYDZPTLP.js +1 -0
- package/dist/esm/chunks/chunk-QDHLXXAZ.js +1 -0
- package/dist/esm/chunks/{chunk-4GCXM764.js → chunk-QUECUXHD.js} +1 -1
- package/dist/esm/chunks/chunk-RJY76ESL.js +1 -0
- package/dist/esm/chunks/chunk-RPI5C3EJ.js +1 -0
- package/dist/esm/chunks/{chunk-BFTZR4OB.js → chunk-YC4JXWRG.js} +2 -2
- package/dist/esm/chunks/{diagnose-3FEKRIEK.js → diagnose-3XHPZZNP.js} +1 -1
- package/dist/esm/chunks/extras-C724KKG6.js +1 -0
- package/dist/esm/chunks/{markets-ui-FD6NI2OG.js → markets-ui-WBWWQAGU.js} +1 -1
- package/dist/esm/chunks/{search-UJ73YBQ5.js → search-5NFIZBPJ.js} +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/server.js +2 -2
- package/dist/tether.account-pages.js +13 -3
- package/dist/tether.account-pages.min.js +1 -1
- package/dist/tether.account.js +13 -3
- package/dist/tether.account.min.js +1 -1
- package/dist/tether.analytics.js +18 -3
- package/dist/tether.analytics.min.js +1 -1
- package/dist/tether.diagnose.js +20 -5
- package/dist/tether.diagnose.min.js +2 -2
- package/dist/tether.extras.js +52 -28
- package/dist/tether.extras.min.js +1 -1
- package/dist/tether.js +353 -288
- package/dist/tether.markets.js +31 -5
- package/dist/tether.markets.min.js +1 -1
- package/dist/tether.min.js +2 -2
- package/dist/tether.min.js.map +4 -4
- package/dist/tether.search.js +18 -3
- package/dist/tether.search.min.js +1 -1
- package/dist/types/server/src/shared/cart-id.d.ts +2 -0
- package/dist/types/src/cart/mutations.d.ts +6 -0
- package/dist/types/src/cart/store.d.ts +5 -2
- package/dist/types/src/cart.d.ts +3 -2
- package/dist/types/src/client.d.ts +61 -22
- package/dist/types/src/config.d.ts +6 -1
- package/dist/types/src/index.d.ts +2 -2
- package/dist/types/src/markets.d.ts +9 -2
- package/dist/types/src/product-shape.d.ts +3 -1
- package/dist/types/src/uri.d.ts +6 -0
- package/package.json +54 -6
- package/dist/esm/chunks/analytics-2V632IRV.js +0 -1
- package/dist/esm/chunks/chunk-56FCLJJD.js +0 -1
- package/dist/esm/chunks/chunk-A5SYCVQM.js +0 -1
- package/dist/esm/chunks/chunk-BA4LQJJQ.js +0 -1
- package/dist/esm/chunks/chunk-C5JYQCWH.js +0 -1
- package/dist/esm/chunks/chunk-E5YO6274.js +0 -1
- package/dist/esm/chunks/chunk-MPDWM2NN.js +0 -1
- package/dist/esm/chunks/chunk-YLUULATY.js +0 -1
- package/dist/esm/chunks/extras-6POFBNOP.js +0 -1
- package/src/account/addresses-view.ts +0 -58
- package/src/account/answers.ts +0 -79
- package/src/account/markup.ts +0 -36
- package/src/account/operations.ts +0 -145
- package/src/account/order-lines.ts +0 -75
- package/src/account/order-list.ts +0 -107
- package/src/account/order-page.ts +0 -137
- package/src/account/page-answers.ts +0 -317
- package/src/account/page-events.ts +0 -253
- package/src/account/server-client.ts +0 -183
- package/src/account/subscriptions-view.ts +0 -115
- package/src/account-api.ts +0 -98
- package/src/account-marks.ts +0 -35
- package/src/account-pages.ts +0 -81
- package/src/account.ts +0 -284
- package/src/amounts.ts +0 -64
- package/src/analytics-shopify.ts +0 -246
- package/src/analytics.ts +0 -315
- package/src/api-version.ts +0 -79
- package/src/astro.ts +0 -272
- package/src/cart/cart-shape.ts +0 -25
- package/src/cart/checkout.ts +0 -101
- package/src/cart/drawer.ts +0 -125
- package/src/cart/mutations.ts +0 -237
- package/src/cart/render.ts +0 -266
- package/src/cart/store.ts +0 -279
- package/src/cart.ts +0 -345
- package/src/client.ts +0 -391
- package/src/collection.ts +0 -175
- package/src/config-sections.ts +0 -284
- package/src/config.ts +0 -480
- package/src/controls/cart-controls.ts +0 -123
- package/src/controls/discount.ts +0 -75
- package/src/controls/extras.ts +0 -54
- package/src/controls/navigation.ts +0 -77
- package/src/diagnose-markup.ts +0 -298
- package/src/diagnose.ts +0 -294
- package/src/dom.ts +0 -268
- package/src/extras.ts +0 -136
- package/src/fragments.ts +0 -261
- package/src/globals.d.ts +0 -15
- package/src/images.ts +0 -91
- package/src/index.ts +0 -415
- package/src/initial.ts +0 -101
- package/src/lazy-iife.ts +0 -197
- package/src/lazy.ts +0 -42
- package/src/loader.ts +0 -363
- package/src/market-codes.ts +0 -6
- package/src/markets-ui.ts +0 -227
- package/src/markets.ts +0 -246
- package/src/markup.ts +0 -127
- package/src/mounting.ts +0 -264
- package/src/page-market.ts +0 -110
- package/src/part-integrity.ts +0 -7
- package/src/parts/account-pages.ts +0 -7
- package/src/parts/account.ts +0 -7
- package/src/parts/analytics.ts +0 -7
- package/src/parts/diagnose.ts +0 -7
- package/src/parts/extras.ts +0 -7
- package/src/parts/markets.ts +0 -7
- package/src/parts/search.ts +0 -7
- package/src/presets/barba.ts +0 -194
- package/src/product-events.ts +0 -115
- package/src/product-render.ts +0 -218
- package/src/product-shape.ts +0 -49
- package/src/product-state.ts +0 -318
- package/src/product.ts +0 -308
- package/src/repeaters.ts +0 -161
- package/src/search.ts +0 -305
- package/src/selling-plans.ts +0 -145
- package/src/server.ts +0 -211
- package/src/shopify-values.ts +0 -91
- package/src/status.ts +0 -106
- package/src/storage.ts +0 -34
- package/src/store-html.ts +0 -359
- package/src/storefront/catalog.ts +0 -196
- package/src/storefront/json-ld.ts +0 -144
- package/src/variant-render.ts +0 -139
package/src/cart.ts
DELETED
|
@@ -1,345 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The cart, as one object: `Tether.cart`. Every change goes through one queue (fast clicks never race) and resolves
|
|
3
|
-
* with the cart Shopify sent back. The state lives in `cart/store.ts`, the Cart API calls in `cart/mutations.ts`, the
|
|
4
|
-
* drawer in `cart/drawer.ts`, the document rendering in `cart/render.ts` and the checkout in `cart/checkout.ts`.
|
|
5
|
-
*/
|
|
6
|
-
import { cfg, reportError } from './config.ts';
|
|
7
|
-
import { TetherError } from './client.ts';
|
|
8
|
-
import { emit, toAttributes } from './dom.ts';
|
|
9
|
-
import {
|
|
10
|
-
fetchCart,
|
|
11
|
-
isCartGone,
|
|
12
|
-
mutateCart,
|
|
13
|
-
removeLines,
|
|
14
|
-
setLineQuantity,
|
|
15
|
-
toLineInput,
|
|
16
|
-
toLineUpdate,
|
|
17
|
-
updateLines,
|
|
18
|
-
} from './cart/mutations.ts';
|
|
19
|
-
import { checkout, setCheckoutResolver } from './cart/checkout.ts';
|
|
20
|
-
import { drawers, isOpen, openCart, closeCart, toggleCart, trapFocus } from './cart/drawer.ts';
|
|
21
|
-
import {
|
|
22
|
-
announce,
|
|
23
|
-
cartStatus,
|
|
24
|
-
commit,
|
|
25
|
-
currentCart,
|
|
26
|
-
currentWarnings,
|
|
27
|
-
enqueue,
|
|
28
|
-
forgetSavedCart,
|
|
29
|
-
keepQuietly,
|
|
30
|
-
pendingQuantities,
|
|
31
|
-
pendingQuantityOf,
|
|
32
|
-
render,
|
|
33
|
-
savedCartId,
|
|
34
|
-
settleLoad,
|
|
35
|
-
showLastKnownCount,
|
|
36
|
-
subscribe,
|
|
37
|
-
} from './cart/store.ts';
|
|
38
|
-
import type { CartWarning } from './config.ts';
|
|
39
|
-
import type { Attributes, LineSpec, CartLineInput, LinePatch } from './cart/mutations.ts';
|
|
40
|
-
import type { ShopifyCart, ShopifyCartLine, CartStatus } from './cart/store.ts';
|
|
41
|
-
|
|
42
|
-
export { trapFocus };
|
|
43
|
-
|
|
44
|
-
type DiscountCodes = string[] | string | ((current: string[]) => string[]);
|
|
45
|
-
|
|
46
|
-
const currentLines = (): readonly ShopifyCartLine[] => currentCart()?.lines.nodes ?? [];
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* +1 / -1 for a line. Quick clicks add up: each builds on the quantity the click before asked for, which shows at once
|
|
50
|
-
* (`tether-line="quantity"`, `quantityOf`) until Shopify has answered. Only the latest target is sent; the ones it
|
|
51
|
-
* replaced are skipped.
|
|
52
|
-
*/
|
|
53
|
-
function changeQuantity(lineId: string, delta: number): Promise<ShopifyCart | null> {
|
|
54
|
-
const line = currentLines().find((candidate) => candidate.id === lineId);
|
|
55
|
-
if (!line) return Promise.resolve(currentCart());
|
|
56
|
-
const target = Math.max(0, pendingQuantityOf(line) + delta);
|
|
57
|
-
const job = { quantity: target };
|
|
58
|
-
pendingQuantities.set(lineId, job);
|
|
59
|
-
announce('pending');
|
|
60
|
-
const isLatest = () => pendingQuantities.get(lineId) === job;
|
|
61
|
-
const sent = enqueue(async () => {
|
|
62
|
-
if (!isLatest()) return currentCart();
|
|
63
|
-
const now = currentLines().find((candidate) => candidate.id === lineId);
|
|
64
|
-
// A + then a -: Shopify already has the quantity.
|
|
65
|
-
if (now?.quantity === target) return currentCart();
|
|
66
|
-
const cart = await setLineQuantity(lineId, target);
|
|
67
|
-
// A newer click is on its way: keep this answer, but show totals and count once that one is in, instead of
|
|
68
|
-
// stepping through every reply.
|
|
69
|
-
if (!isLatest()) {
|
|
70
|
-
keepQuietly(cart);
|
|
71
|
-
return cart;
|
|
72
|
-
}
|
|
73
|
-
return commit(cart, target > 0 ? 'update' : 'remove');
|
|
74
|
-
});
|
|
75
|
-
return sent.finally(() => {
|
|
76
|
-
if (!isLatest()) return;
|
|
77
|
-
pendingQuantities.delete(lineId);
|
|
78
|
-
announce('settled');
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* The codes the cart has once a change is its turn in the queue.
|
|
84
|
-
*
|
|
85
|
-
* @param codes A function gets the codes as they are by then.
|
|
86
|
-
*/
|
|
87
|
-
function discountCodesFrom(codes: DiscountCodes): string[] {
|
|
88
|
-
const current = (currentCart()?.discountCodes ?? []).map((discount) => discount.code);
|
|
89
|
-
const list = typeof codes === 'function' ? codes(current) : codes;
|
|
90
|
-
return (Array.isArray(list) ? list : [list]).map((code) => code.trim()).filter(Boolean);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** @returns The cart's attributes with `attributes` merged in; `''` removes a key. */
|
|
94
|
-
function mergedAttributes(attributes: Attributes): { key: string; value: string }[] {
|
|
95
|
-
const merged = new Map((currentCart()?.attributes ?? []).map((attribute) => [attribute.key, attribute.value]));
|
|
96
|
-
toAttributes(attributes).forEach((attribute) => merged.set(attribute.key, attribute.value));
|
|
97
|
-
return [...merged].filter(([, value]) => value !== '').map(([key, value]) => ({ key, value }));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Loads the saved cart. A cart Shopify no longer has is forgotten; one it can't be asked about (the network, the
|
|
102
|
-
* token) is kept, because it is probably fine.
|
|
103
|
-
*/
|
|
104
|
-
async function init(): Promise<ShopifyCart | null> {
|
|
105
|
-
const id = savedCartId();
|
|
106
|
-
if (!id) {
|
|
107
|
-
settleLoad({ kind: 'loaded', cart: null });
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
showLastKnownCount();
|
|
111
|
-
try {
|
|
112
|
-
const cart = await fetchCart(id);
|
|
113
|
-
if (!cart) forgetSavedCart();
|
|
114
|
-
settleLoad({ kind: 'loaded', cart });
|
|
115
|
-
} catch (error) {
|
|
116
|
-
reportError(error, 'cart load failed');
|
|
117
|
-
if (isCartGone(error)) forgetSavedCart();
|
|
118
|
-
settleLoad({ kind: 'failed', error });
|
|
119
|
-
}
|
|
120
|
-
return currentCart();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
export const Cart = {
|
|
124
|
-
init,
|
|
125
|
-
/** The saved cart's id. */
|
|
126
|
-
id: savedCartId,
|
|
127
|
-
|
|
128
|
-
get status(): CartStatus {
|
|
129
|
-
return cartStatus();
|
|
130
|
-
},
|
|
131
|
-
get state(): ShopifyCart | null {
|
|
132
|
-
return currentCart();
|
|
133
|
-
},
|
|
134
|
-
/** The cart's lines, a copy: changing it changes nothing in the cart. */
|
|
135
|
-
get lines(): ShopifyCartLine[] {
|
|
136
|
-
return [...currentLines()];
|
|
137
|
-
},
|
|
138
|
-
get count(): number {
|
|
139
|
-
return currentCart()?.totalQuantity ?? 0;
|
|
140
|
-
},
|
|
141
|
-
/** True once the saved cart, or that there is none, is known. */
|
|
142
|
-
get loaded(): boolean {
|
|
143
|
-
return cartStatus().kind !== 'unknown';
|
|
144
|
-
},
|
|
145
|
-
/**
|
|
146
|
-
* The failure of the last load or change, until a change succeeds. Requests fail with an `Error`; anything else
|
|
147
|
-
* thrown is wrapped, so the public type holds.
|
|
148
|
-
*/
|
|
149
|
-
get lastError(): Error | null {
|
|
150
|
-
const current = cartStatus();
|
|
151
|
-
if (current.kind !== 'error') return null;
|
|
152
|
-
return current.error instanceof Error ? current.error : new Error(String(current.error));
|
|
153
|
-
},
|
|
154
|
-
/** Shopify's notes on the last change, a copy. */
|
|
155
|
-
get warnings(): CartWarning[] {
|
|
156
|
-
return [...currentWarnings()];
|
|
157
|
-
},
|
|
158
|
-
/** Line id → the quantity quick clicks asked for. */
|
|
159
|
-
get pendingQty(): ReadonlyMap<string, { quantity: number }> {
|
|
160
|
-
return pendingQuantities;
|
|
161
|
-
},
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Sets where a logged-in customer checks out, or clears it with `null`: the account part's resolver answers the
|
|
165
|
-
* customer's checkout URL for the cart (`null` uses the cart's own).
|
|
166
|
-
*/
|
|
167
|
-
setCheckoutResolver,
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Forgets the saved cart on this device, as if there were none: the page shows an empty cart and
|
|
171
|
-
* `tether:cart-updated` fires with reason `load`. The next add starts a new cart.
|
|
172
|
-
*/
|
|
173
|
-
forgetSaved(): void {
|
|
174
|
-
forgetSavedCart();
|
|
175
|
-
settleLoad({ kind: 'loaded', cart: null });
|
|
176
|
-
},
|
|
177
|
-
|
|
178
|
-
render,
|
|
179
|
-
subscribe,
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* `add(variantId, 2)`, `add({ variantId, quantity, sellingPlanId, attributes })`, or `add([line, line])` for
|
|
183
|
-
* bundles in one request. Creates the cart when there is none.
|
|
184
|
-
*
|
|
185
|
-
* @throws {TetherError} (rejected) When the add fails, or Shopify answers it without a cart.
|
|
186
|
-
*/
|
|
187
|
-
add(
|
|
188
|
-
input: string | number | LineSpec | (string | number | LineSpec)[],
|
|
189
|
-
quantity = 1,
|
|
190
|
-
options: Omit<LineSpec, 'variantId' | 'quantity'> = {},
|
|
191
|
-
): Promise<ShopifyCart> {
|
|
192
|
-
const lines: CartLineInput[] = (Array.isArray(input) ? input : [input]).map((line) =>
|
|
193
|
-
toLineInput(line, quantity, options),
|
|
194
|
-
);
|
|
195
|
-
return enqueue(async () => {
|
|
196
|
-
const cart = await mutateCart({
|
|
197
|
-
name: 'cartLinesAdd',
|
|
198
|
-
args: '$lines: [CartLineInput!]!',
|
|
199
|
-
inner: 'lines: $lines',
|
|
200
|
-
variables: { lines },
|
|
201
|
-
createInput: { lines },
|
|
202
|
-
});
|
|
203
|
-
// A mutation answers without a cart only beside user errors, which reject above; an add always has one.
|
|
204
|
-
if (!cart) throw new TetherError('Shopify answered the add without a cart');
|
|
205
|
-
commit(cart, 'add');
|
|
206
|
-
emit('added', { cart, lines });
|
|
207
|
-
if (cfg.openCartOnAdd) openCart();
|
|
208
|
-
return cart;
|
|
209
|
-
});
|
|
210
|
-
},
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* `updateLine(lineId, 3)` or `updateLine(lineId, { quantity, sellingPlanId, attributes })`.
|
|
214
|
-
*
|
|
215
|
-
* @param patch A quantity of 0 or less removes the line.
|
|
216
|
-
*/
|
|
217
|
-
updateLine(lineId: string, patch: number | LinePatch): Promise<ShopifyCart | null> {
|
|
218
|
-
const quantity = typeof patch === 'number' ? patch : patch.quantity;
|
|
219
|
-
if (quantity !== undefined && quantity <= 0) return Cart.removeLine(lineId);
|
|
220
|
-
const line = toLineUpdate(lineId, patch);
|
|
221
|
-
return enqueue(async () => commit(await updateLines([line]), 'update'));
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
changeQuantity,
|
|
225
|
-
|
|
226
|
-
/** The quantity to show: the one asked for while an update is on its way. */
|
|
227
|
-
quantityOf: pendingQuantityOf,
|
|
228
|
-
|
|
229
|
-
removeLine(lineIds: string | string[]): Promise<ShopifyCart | null> {
|
|
230
|
-
const ids = Array.isArray(lineIds) ? lineIds : [lineIds];
|
|
231
|
-
return enqueue(async () => commit(await removeLines(ids), 'remove'));
|
|
232
|
-
},
|
|
233
|
-
|
|
234
|
-
clear(): Promise<ShopifyCart | null> {
|
|
235
|
-
const ids = currentLines().map((line) => line.id);
|
|
236
|
-
if (!ids.length) return Promise.resolve(currentCart());
|
|
237
|
-
return Cart.removeLine(ids);
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
setNote(note: string | null | undefined): Promise<ShopifyCart | null> {
|
|
241
|
-
const variables = { note: note ?? '' };
|
|
242
|
-
return enqueue(async () =>
|
|
243
|
-
commit(
|
|
244
|
-
await mutateCart({
|
|
245
|
-
name: 'cartNoteUpdate',
|
|
246
|
-
args: '$note: String!',
|
|
247
|
-
inner: 'note: $note',
|
|
248
|
-
variables,
|
|
249
|
-
createInput: variables,
|
|
250
|
-
}),
|
|
251
|
-
'note',
|
|
252
|
-
),
|
|
253
|
-
);
|
|
254
|
-
},
|
|
255
|
-
|
|
256
|
-
/**
|
|
257
|
-
* Cart attributes, merged with the cart's (as it is when the change runs, so quick calls don't drop each other).
|
|
258
|
-
*
|
|
259
|
-
* @param attributes The value `''` removes a key.
|
|
260
|
-
*/
|
|
261
|
-
setAttributes(attributes: Attributes): Promise<ShopifyCart | null> {
|
|
262
|
-
return enqueue(async () => {
|
|
263
|
-
const variables = { attributes: mergedAttributes(attributes) };
|
|
264
|
-
const cart = await mutateCart({
|
|
265
|
-
name: 'cartAttributesUpdate',
|
|
266
|
-
args: '$attributes: [AttributeInput!]!',
|
|
267
|
-
inner: 'attributes: $attributes',
|
|
268
|
-
variables,
|
|
269
|
-
createInput: variables,
|
|
270
|
-
});
|
|
271
|
-
return commit(cart, 'attributes');
|
|
272
|
-
});
|
|
273
|
-
},
|
|
274
|
-
|
|
275
|
-
/** Replaces the discount codes; `[]` removes them all. */
|
|
276
|
-
setDiscountCodes(codes: DiscountCodes): Promise<ShopifyCart | null> {
|
|
277
|
-
return enqueue(async () => {
|
|
278
|
-
const variables = { discountCodes: discountCodesFrom(codes) };
|
|
279
|
-
const cart = await mutateCart({
|
|
280
|
-
name: 'cartDiscountCodesUpdate',
|
|
281
|
-
args: '$discountCodes: [String!]!',
|
|
282
|
-
inner: 'discountCodes: $discountCodes',
|
|
283
|
-
variables,
|
|
284
|
-
createInput: variables,
|
|
285
|
-
});
|
|
286
|
-
return commit(cart, 'discount');
|
|
287
|
-
});
|
|
288
|
-
},
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Adds a code. A code Shopify doesn't accept is removed again, so it doesn't warn on every later change.
|
|
292
|
-
*
|
|
293
|
-
* @throws {TetherError} `INVALID_DISCOUNT` (rejected) when Shopify doesn't accept the code.
|
|
294
|
-
*/
|
|
295
|
-
async applyDiscount(code: string): Promise<ShopifyCart | null> {
|
|
296
|
-
const cart = await Cart.setDiscountCodes((current) => [...new Set([...current, code])]);
|
|
297
|
-
const wanted = code.toLowerCase();
|
|
298
|
-
const applied = (cart?.discountCodes ?? []).find((discount) => discount.code.toLowerCase() === wanted);
|
|
299
|
-
if (applied?.applicable) return cart;
|
|
300
|
-
try {
|
|
301
|
-
await Cart.removeDiscount(code);
|
|
302
|
-
} catch (error) {
|
|
303
|
-
reportError(error, 'removing a discount code Shopify refused');
|
|
304
|
-
}
|
|
305
|
-
throw new TetherError(`Discount code not valid: ${code}`, { code: 'INVALID_DISCOUNT' });
|
|
306
|
-
},
|
|
307
|
-
|
|
308
|
-
removeDiscount(code: string): Promise<ShopifyCart | null> {
|
|
309
|
-
const unwanted = code.toLowerCase();
|
|
310
|
-
return Cart.setDiscountCodes((current) => current.filter((existing) => existing.toLowerCase() !== unwanted));
|
|
311
|
-
},
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Sets who is buying; a country changes the cart's currency and prices to that market's.
|
|
315
|
-
*
|
|
316
|
-
* @param options `create: false` forgets an expired cart instead of creating an empty one.
|
|
317
|
-
*/
|
|
318
|
-
setBuyerIdentity(
|
|
319
|
-
buyerIdentity: { countryCode?: string; email?: string; phone?: string },
|
|
320
|
-
{ create = true }: { create?: boolean } = {},
|
|
321
|
-
): Promise<ShopifyCart | null> {
|
|
322
|
-
return enqueue(async () => {
|
|
323
|
-
const cart = await mutateCart({
|
|
324
|
-
name: 'cartBuyerIdentityUpdate',
|
|
325
|
-
args: '$buyerIdentity: CartBuyerIdentityInput!',
|
|
326
|
-
inner: 'buyerIdentity: $buyerIdentity',
|
|
327
|
-
variables: { buyerIdentity },
|
|
328
|
-
createInput: create ? { buyerIdentity } : null,
|
|
329
|
-
}).catch((error: unknown) => {
|
|
330
|
-
// mutateCart has forgotten the cart Shopify no longer has; without `create` that leaves no cart.
|
|
331
|
-
if (create || !isCartGone(error)) throw error;
|
|
332
|
-
return null;
|
|
333
|
-
});
|
|
334
|
-
return commit(cart, 'buyer-identity');
|
|
335
|
-
});
|
|
336
|
-
},
|
|
337
|
-
|
|
338
|
-
checkout,
|
|
339
|
-
|
|
340
|
-
drawers,
|
|
341
|
-
isOpen,
|
|
342
|
-
open: openCart,
|
|
343
|
-
close: closeCart,
|
|
344
|
-
toggle: toggleCart,
|
|
345
|
-
};
|