@usethrottle/catalog 1.2.0 → 1.4.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/CHANGELOG.md +27 -0
- package/README.md +111 -15
- package/dist/admin.cjs +16 -16
- package/dist/admin.d.cts +15 -2
- package/dist/admin.d.ts +15 -2
- package/dist/admin.js +1 -1
- package/dist/{catalog-error-yTBMAdCv.d.cts → catalog-error-BKN5hNWP.d.cts} +11 -1
- package/dist/{catalog-error-7DCsTyB2.d.ts → catalog-error-BnabMMEy.d.ts} +11 -1
- package/dist/{chunk-JNBKKAMP.js → chunk-3FX2HISB.js} +97 -2
- package/dist/{chunk-B2DYAHNR.cjs → chunk-J2GHGQY6.cjs} +334 -43
- package/dist/{chunk-TDMDOHD3.cjs → chunk-OKWXS3FD.cjs} +110 -15
- package/dist/chunk-PZ3SFYH4.cjs +192 -0
- package/dist/chunk-SN6STYPW.js +192 -0
- package/dist/{chunk-55G4ZTD5.js → chunk-VJHA7J7B.js} +315 -24
- package/dist/{client-Dm6I4nAZ.d.ts → client-B6CsU_v9.d.ts} +1 -1
- package/dist/{client-BrHJCyBP.d.cts → client-terkEl6u.d.cts} +1 -1
- package/dist/{contract-types-BPSzY_Co.d.cts → contract-types-D2-SSPM9.d.cts} +241 -11
- package/dist/{contract-types-BPSzY_Co.d.ts → contract-types-D2-SSPM9.d.ts} +241 -11
- package/dist/index.cjs +34 -2
- package/dist/index.d.cts +56 -7
- package/dist/index.d.ts +56 -7
- package/dist/index.js +35 -3
- package/dist/next.cjs +180 -0
- package/dist/next.d.cts +60 -0
- package/dist/next.d.ts +60 -0
- package/dist/next.js +180 -0
- package/dist/react-extras.cjs +27 -9
- package/dist/react-extras.d.cts +7 -4
- package/dist/react-extras.d.ts +7 -4
- package/dist/react-extras.js +20 -2
- package/dist/react.cjs +130 -54
- package/dist/react.d.cts +52 -11
- package/dist/react.d.ts +52 -11
- package/dist/react.js +112 -36
- package/dist/server.cjs +58 -66
- package/dist/server.d.cts +58 -10
- package/dist/server.d.ts +58 -10
- package/dist/server.js +52 -60
- package/dist/{types-BlYCKzB8.d.ts → types-jNpOwEUw.d.cts} +81 -8
- package/dist/{types-BTrqSprj.d.cts → types-z57bkjjB.d.ts} +81 -8
- package/dist/url.d.cts +2 -1
- package/dist/url.d.ts +2 -1
- package/dist/{use-query-QOEBuYlp.d.ts → use-query-COqRZJnC.d.ts} +1 -1
- package/dist/{use-query-BZgjxGPO.d.cts → use-query-H1xgL3_e.d.cts} +1 -1
- package/package.json +17 -2
package/dist/server.js
CHANGED
|
@@ -1,96 +1,84 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
DEFAULT_CACHE_POLICY,
|
|
3
|
+
cacheKindForPath,
|
|
4
|
+
catalogTags,
|
|
5
|
+
observeGeneration,
|
|
6
|
+
pendingMinGeneration,
|
|
7
|
+
policyToNext,
|
|
8
|
+
scopesToTags,
|
|
9
|
+
storefrontRoute,
|
|
10
|
+
tagsForUrl
|
|
11
|
+
} from "./chunk-SN6STYPW.js";
|
|
12
|
+
import {
|
|
3
13
|
CATEGORY_TREE_MAX_DEPTH,
|
|
4
14
|
CatalogError,
|
|
5
15
|
DEFAULT_BASE_URL,
|
|
6
|
-
DEFAULT_REVALIDATE_S,
|
|
7
16
|
LIST_MAX_HANDLES,
|
|
8
17
|
LIST_PAGE_SIZE,
|
|
9
18
|
SDK_VERSION,
|
|
10
|
-
TAG_MAX_CHARS,
|
|
11
19
|
createCatalogClient
|
|
12
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-VJHA7J7B.js";
|
|
13
21
|
import "./chunk-PKBMQBKP.js";
|
|
14
22
|
|
|
15
|
-
// src/server/tags.ts
|
|
16
|
-
function catalogTags(route, handle) {
|
|
17
|
-
const tags = [CACHE_TAG, `${CACHE_TAG}:${route}`];
|
|
18
|
-
if (handle !== void 0) tags.push(`${CACHE_TAG}:${route}:${handle}`);
|
|
19
|
-
return tags.filter((tag) => tag.length <= TAG_MAX_CHARS);
|
|
20
|
-
}
|
|
21
|
-
function decode(segment) {
|
|
22
|
-
try {
|
|
23
|
-
return decodeURIComponent(segment);
|
|
24
|
-
} catch {
|
|
25
|
-
return segment;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function tagsForUrl(url, storefront) {
|
|
29
|
-
const path = url.split(/[?#]/u, 1)[0] ?? "";
|
|
30
|
-
if (!path.startsWith(storefront)) return null;
|
|
31
|
-
const parts = path.slice(storefront.length).split("/");
|
|
32
|
-
if (parts.length > 3) return null;
|
|
33
|
-
const [first, segment, sub] = parts;
|
|
34
|
-
if (segment === void 0) {
|
|
35
|
-
switch (first) {
|
|
36
|
-
case "config":
|
|
37
|
-
case "products":
|
|
38
|
-
case "categories":
|
|
39
|
-
case "tags":
|
|
40
|
-
case "collections":
|
|
41
|
-
case "suggest":
|
|
42
|
-
return catalogTags(first);
|
|
43
|
-
default:
|
|
44
|
-
return null;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
const handle = decode(segment);
|
|
48
|
-
if (handle === "") return null;
|
|
49
|
-
switch (first) {
|
|
50
|
-
case "filters":
|
|
51
|
-
return sub === "values" ? catalogTags("filters") : null;
|
|
52
|
-
case "products":
|
|
53
|
-
if (sub === "related" || sub === "reviews")
|
|
54
|
-
return catalogTags(sub, handle);
|
|
55
|
-
return sub === void 0 ? catalogTags("product", handle) : null;
|
|
56
|
-
case "categories":
|
|
57
|
-
return sub === void 0 ? catalogTags("category", handle) : null;
|
|
58
|
-
case "collections":
|
|
59
|
-
return sub === void 0 ? catalogTags("collection", handle) : null;
|
|
60
|
-
default:
|
|
61
|
-
return null;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
23
|
// src/server/client.ts
|
|
66
|
-
function serverInit(url, init, storefront,
|
|
24
|
+
function serverInit(url, init, storefront, policies, extraTags) {
|
|
67
25
|
const { next, ...rest } = init;
|
|
68
26
|
const tags = rest.method === "GET" ? tagsForUrl(url, storefront) : null;
|
|
69
27
|
if (tags === null || rest.cache === "no-store")
|
|
70
28
|
return { ...rest, cache: "no-store" };
|
|
71
29
|
if (next !== void 0) return init;
|
|
72
|
-
|
|
30
|
+
const kind = cacheKindForPath(url.slice(storefront.length));
|
|
31
|
+
const policy = kind === null ? "no-store" : policies(kind);
|
|
32
|
+
if (policy === "no-store") return { ...rest, cache: "no-store" };
|
|
33
|
+
return {
|
|
34
|
+
...rest,
|
|
35
|
+
next: { revalidate: policy.revalidate, tags: [...tags, ...extraTags] }
|
|
36
|
+
};
|
|
73
37
|
}
|
|
74
38
|
function createServerCatalogClient(options) {
|
|
75
39
|
const {
|
|
76
40
|
next,
|
|
41
|
+
cachePolicy,
|
|
42
|
+
floorKey: namedFloorKey,
|
|
77
43
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars -- dropped on purpose (P8-R21)
|
|
78
44
|
keyInQuery: _ignored,
|
|
79
45
|
...rest
|
|
80
46
|
} = options;
|
|
81
|
-
const
|
|
47
|
+
const named = /* @__PURE__ */ new Map();
|
|
48
|
+
for (const [kind, policy] of Object.entries(cachePolicy ?? {}))
|
|
49
|
+
if (policy !== void 0)
|
|
50
|
+
named.set(kind, policyToNext(policy));
|
|
51
|
+
const policies = (kind) => named.get(kind) ?? (next?.revalidate === void 0 ? policyToNext(DEFAULT_CACHE_POLICY[kind]) : { revalidate: next.revalidate });
|
|
52
|
+
const floorKey = namedFloorKey ?? rest.publishableKey;
|
|
82
53
|
const extraTags = next?.tags ?? [];
|
|
83
54
|
const storefront = `${(rest.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/u, "")}/v1/storefront/`;
|
|
84
55
|
const base = rest.fetch;
|
|
85
56
|
const serverFetch = (input, init = {}) => {
|
|
57
|
+
let target = String(input);
|
|
58
|
+
const isConfig = init.method === "GET" && target.startsWith(storefront) && storefrontRoute(target.slice(storefront.length))?.route === "config";
|
|
59
|
+
const floor = isConfig ? pendingMinGeneration(floorKey) : void 0;
|
|
60
|
+
if (floor !== void 0 && !target.includes("minGeneration="))
|
|
61
|
+
target += `${target.includes("?") ? "&" : "?"}minGeneration=${floor}`;
|
|
86
62
|
const out = serverInit(
|
|
87
|
-
|
|
63
|
+
target,
|
|
88
64
|
init,
|
|
89
65
|
storefront,
|
|
90
|
-
|
|
66
|
+
policies,
|
|
91
67
|
extraTags
|
|
92
68
|
);
|
|
93
|
-
|
|
69
|
+
const answer = base !== void 0 ? base(target, out) : globalThis.fetch(target, out);
|
|
70
|
+
if (floor === void 0) return answer;
|
|
71
|
+
return answer.then(async (response) => {
|
|
72
|
+
if (response.ok)
|
|
73
|
+
try {
|
|
74
|
+
observeGeneration(
|
|
75
|
+
(await response.clone().json()).generation,
|
|
76
|
+
floorKey
|
|
77
|
+
);
|
|
78
|
+
} catch {
|
|
79
|
+
}
|
|
80
|
+
return response;
|
|
81
|
+
});
|
|
94
82
|
};
|
|
95
83
|
return createCatalogClient({
|
|
96
84
|
...rest,
|
|
@@ -205,12 +193,16 @@ async function listAllCategoryHandles(client, opts = {}) {
|
|
|
205
193
|
return out;
|
|
206
194
|
}
|
|
207
195
|
export {
|
|
196
|
+
DEFAULT_CACHE_POLICY,
|
|
208
197
|
SDK_VERSION,
|
|
198
|
+
cacheKindForPath,
|
|
209
199
|
catalogTags,
|
|
210
200
|
createServerCatalogClient,
|
|
211
201
|
listAllCategoryHandles,
|
|
212
202
|
listAllCollectionHandles,
|
|
213
203
|
listAllProductHandles,
|
|
204
|
+
policyToNext,
|
|
205
|
+
scopesToTags,
|
|
214
206
|
staticProductParams,
|
|
215
207
|
syncCatalogGeneration
|
|
216
208
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CurrencyCode } from '@usethrottle/money';
|
|
2
|
+
import { C as CartCheckAnswer, j as CartCreateRequest, i as CartCreateAnswer, d as StorefrontListAnswer, g as StorefrontProductAnswer, u as StorefrontRelatedAnswer, r as StorefrontFilterValuesAnswer, l as StorefrontCategoriesAnswer, m as StorefrontCategoryAnswer, x as StorefrontTagsAnswer, o as StorefrontCollectionsAnswer, n as StorefrontCollectionAnswer, w as StorefrontSuggestAnswer, p as StorefrontConfigAnswer, S as StorefrontSettings, t as StorefrontPagesAnswer, s as StorefrontPageAnswer, v as StorefrontReviewsAnswer, f as ReviewEligibilityAnswer, a as ReviewSubmitInput, R as ReviewSubmitAnswer } from './contract-types-D2-SSPM9.cjs';
|
|
2
3
|
|
|
3
4
|
type QueryPairs = [string, string][];
|
|
4
5
|
|
|
@@ -35,6 +36,13 @@ type CatalogClientOptions = {
|
|
|
35
36
|
* the header.
|
|
36
37
|
*/
|
|
37
38
|
keyInQuery?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* A preview token (from the dashboard's preview link). Settings and pages
|
|
41
|
+
* reads then send it as `X-Storefront-Preview` and answer the draft: they
|
|
42
|
+
* are never cached, here or in the `cache` store. A token that does not
|
|
43
|
+
* verify is a `401` `PREVIEW_EXPIRED`. Other routes never carry it.
|
|
44
|
+
*/
|
|
45
|
+
previewToken?: string;
|
|
38
46
|
};
|
|
39
47
|
/** Next.js's `fetch` cache options; the SDK never imports Next. */
|
|
40
48
|
type NextFetchOptions = {
|
|
@@ -56,6 +64,14 @@ type CallOptions = {
|
|
|
56
64
|
*/
|
|
57
65
|
next?: NextFetchOptions;
|
|
58
66
|
};
|
|
67
|
+
/** Options of the reads that answer settings (`config`, `settings`, `pages`). */
|
|
68
|
+
type SettingsCallOptions = CallOptions & {
|
|
69
|
+
/**
|
|
70
|
+
* The storefront generation the caller knows exists (a change notice names
|
|
71
|
+
* it). Above the one the edge holds, the answer is read fresh.
|
|
72
|
+
*/
|
|
73
|
+
minGeneration?: number;
|
|
74
|
+
};
|
|
59
75
|
type RequestSpec = {
|
|
60
76
|
method: 'GET' | 'POST';
|
|
61
77
|
/** Already encoded, starting with `/`. */
|
|
@@ -84,6 +100,8 @@ type RequestSpec = {
|
|
|
84
100
|
* options). A POST takes them in its body, from `context()`.
|
|
85
101
|
*/
|
|
86
102
|
context?: 'priced' | 'locale';
|
|
103
|
+
/** The route answers a draft to a preview token, so the client sends one. */
|
|
104
|
+
preview?: boolean;
|
|
87
105
|
};
|
|
88
106
|
|
|
89
107
|
/** A list sort the catalog knows. */
|
|
@@ -141,7 +159,7 @@ type ThrottleLine = {
|
|
|
141
159
|
/** Throttle's cart as a cart session answers it; other keys pass through. */
|
|
142
160
|
type ThrottleCart = {
|
|
143
161
|
cartId: string;
|
|
144
|
-
currency:
|
|
162
|
+
currency: CurrencyCode;
|
|
145
163
|
subtotal: number;
|
|
146
164
|
total: number;
|
|
147
165
|
unitCount: number;
|
|
@@ -198,6 +216,32 @@ type CartCheckout = {
|
|
|
198
216
|
};
|
|
199
217
|
/** Per-call options of a cart-session call. */
|
|
200
218
|
type SessionCallOptions = Pick<CallOptions, 'signal' | 'timeoutMs'>;
|
|
219
|
+
/**
|
|
220
|
+
* What an absorb reports besides the cart (MC2-R39): lines of the other cart
|
|
221
|
+
* that could not move (no price in this cart's currency, or a custom line in
|
|
222
|
+
* another currency), a discount code this cart's currency refuses, and lines
|
|
223
|
+
* whose prices are unconfirmed until the next refresh or checkout.
|
|
224
|
+
*/
|
|
225
|
+
type CartWarning = {
|
|
226
|
+
code: 'lines_removed';
|
|
227
|
+
lines: {
|
|
228
|
+
referenceId: string | null;
|
|
229
|
+
code: string;
|
|
230
|
+
}[];
|
|
231
|
+
message: string;
|
|
232
|
+
} | {
|
|
233
|
+
code: 'discount_dropped';
|
|
234
|
+
discountCode: string;
|
|
235
|
+
message: string;
|
|
236
|
+
} | {
|
|
237
|
+
code: 'lines_stale';
|
|
238
|
+
message: string;
|
|
239
|
+
};
|
|
240
|
+
/** `CartSession.absorb()`'s answer: this session's cart after the move. */
|
|
241
|
+
type CartAbsorb = {
|
|
242
|
+
cart: ThrottleCart;
|
|
243
|
+
warnings: CartWarning[];
|
|
244
|
+
};
|
|
201
245
|
/** One Throttle cart session, addressed by its opaque id. */
|
|
202
246
|
type CartSession = {
|
|
203
247
|
readonly id: string;
|
|
@@ -212,6 +256,15 @@ type CartSession = {
|
|
|
212
256
|
refresh(call?: SessionCallOptions): Promise<CartSessionSnapshot>;
|
|
213
257
|
/** Refuses non-http(s) URLs before a request, and never returns a non-http(s) `checkoutUrl`. */
|
|
214
258
|
checkout(input: CartCheckoutInput, call?: SessionCallOptions): Promise<CartCheckout>;
|
|
259
|
+
/**
|
|
260
|
+
* Moves the cart of session `fromSessionId` into this session's cart, in
|
|
261
|
+
* this cart's currency, and closes the other one (`abandoned`, reason
|
|
262
|
+
* `merged`; a merged cart refuses writes with `409 cart_not_open`).
|
|
263
|
+
* Throttle never creates a cart or changes a currency or an owner here: a
|
|
264
|
+
* customer's cart only moves into that customer's cart (`403
|
|
265
|
+
* cart_owner_mismatch`).
|
|
266
|
+
*/
|
|
267
|
+
absorb(fromSessionId: string, call?: SessionCallOptions): Promise<CartAbsorb>;
|
|
215
268
|
};
|
|
216
269
|
type ProductsNamespace = {
|
|
217
270
|
list(query?: ProductsQuery, call?: CallOptions): Promise<StorefrontListAnswer>;
|
|
@@ -273,6 +326,24 @@ type CartsNamespace = {
|
|
|
273
326
|
throttleBaseUrl?: string;
|
|
274
327
|
}): CartSession;
|
|
275
328
|
};
|
|
329
|
+
/** `settings()`: the storefront settings and what versions them. */
|
|
330
|
+
type StorefrontSettingsResult = {
|
|
331
|
+
settings: StorefrontSettings;
|
|
332
|
+
settingsVersion: number;
|
|
333
|
+
generation: number;
|
|
334
|
+
};
|
|
335
|
+
/**
|
|
336
|
+
* `client.settings()`. The document may later be assembled from more than one
|
|
337
|
+
* extension (site design and catalog display); callers always read it through
|
|
338
|
+
* here, so its source can move without a change in the store.
|
|
339
|
+
*/
|
|
340
|
+
type SettingsNamespace = (call?: SettingsCallOptions) => Promise<StorefrontSettingsResult>;
|
|
341
|
+
type PagesNamespace = {
|
|
342
|
+
/** The published pages, in the store's order. */
|
|
343
|
+
list(call?: SettingsCallOptions): Promise<StorefrontPagesAnswer>;
|
|
344
|
+
/** One page; an unknown handle is a `404` `PAGE_NOT_FOUND`. */
|
|
345
|
+
get(handle: string, call?: SettingsCallOptions): Promise<StorefrontPageAnswer>;
|
|
346
|
+
};
|
|
276
347
|
type CatalogNamespaces = {
|
|
277
348
|
readonly products: ProductsNamespace;
|
|
278
349
|
readonly filters: FiltersNamespace;
|
|
@@ -282,7 +353,9 @@ type CatalogNamespaces = {
|
|
|
282
353
|
readonly suggest: (q: string, opts?: {
|
|
283
354
|
limit?: number;
|
|
284
355
|
} & CallOptions) => Promise<StorefrontSuggestAnswer>;
|
|
285
|
-
readonly config: (call?:
|
|
356
|
+
readonly config: (call?: SettingsCallOptions) => Promise<StorefrontConfigAnswer>;
|
|
357
|
+
readonly settings: SettingsNamespace;
|
|
358
|
+
readonly pages: PagesNamespace;
|
|
286
359
|
readonly reviews: ReviewsNamespace;
|
|
287
360
|
readonly carts: CartsNamespace;
|
|
288
361
|
};
|
|
@@ -290,7 +363,7 @@ type CatalogNamespaces = {
|
|
|
290
363
|
type OpenCartRef = {
|
|
291
364
|
sessionId: string;
|
|
292
365
|
cartId: string;
|
|
293
|
-
currency:
|
|
366
|
+
currency: CurrencyCode;
|
|
294
367
|
};
|
|
295
368
|
/**
|
|
296
369
|
* Where a signed-in customer's cart is found (P8-R14). Structural: the SDK
|
|
@@ -299,9 +372,9 @@ type OpenCartRef = {
|
|
|
299
372
|
* `currency`, or `null`; `whenClaimSettled` resolves once the sign-in's
|
|
300
373
|
* guest-cart claim is done, so the merged lines can be read.
|
|
301
374
|
* Resuming needs a currency: the hook's `currency` option or the client's;
|
|
302
|
-
* without one `useCart` makes no lookup.
|
|
303
|
-
*
|
|
304
|
-
*
|
|
375
|
+
* without one `useCart` makes no lookup. A write to a cart another device
|
|
376
|
+
* merged is refused (`409 cart_not_open`, never reopened), and `useCart`
|
|
377
|
+
* then looks the customer's cart up.
|
|
305
378
|
*/
|
|
306
379
|
type CustomerCartSource = {
|
|
307
380
|
getOpenCart(input: {
|
|
@@ -310,4 +383,4 @@ type CustomerCartSource = {
|
|
|
310
383
|
whenClaimSettled(): Promise<void>;
|
|
311
384
|
};
|
|
312
385
|
|
|
313
|
-
export type { CustomerCartSource as C, FieldsOption as F, NextFetchOptions as N, OpenCartRef as O, ProductsQuery as P, Redirect as R, SessionCallOptions as S, ThrottleCart as T, CategoryTreeDepth as a,
|
|
386
|
+
export type { TagsNamespace as A, ThrottleLine as B, CustomerCartSource as C, RequestSpec as D, FieldsOption as F, NextFetchOptions as N, OpenCartRef as O, ProductsQuery as P, Redirect as R, SessionCallOptions as S, ThrottleCart as T, CategoryTreeDepth as a, CartWarning as b, CallOptions as c, CartAbsorb as d, CartCheckout as e, CartCheckoutInput as f, CartSession as g, CartSessionBranding as h, CartSessionDetail as i, CartSessionSnapshot as j, CartsNamespace as k, CatalogClientOptions as l, CatalogHttpCache as m, CatalogHttpCacheEntry as n, CatalogNamespaces as o, CategoriesNamespace as p, CollectionsNamespace as q, CustomerTokenSource as r, FiltersNamespace as s, PagesNamespace as t, ProductsNamespace as u, ReviewsNamespace as v, SettingsCallOptions as w, SettingsNamespace as x, StorefrontSettingsResult as y, StorefrontSort as z };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CurrencyCode } from '@usethrottle/money';
|
|
2
|
+
import { C as CartCheckAnswer, j as CartCreateRequest, i as CartCreateAnswer, d as StorefrontListAnswer, g as StorefrontProductAnswer, u as StorefrontRelatedAnswer, r as StorefrontFilterValuesAnswer, l as StorefrontCategoriesAnswer, m as StorefrontCategoryAnswer, x as StorefrontTagsAnswer, o as StorefrontCollectionsAnswer, n as StorefrontCollectionAnswer, w as StorefrontSuggestAnswer, p as StorefrontConfigAnswer, S as StorefrontSettings, t as StorefrontPagesAnswer, s as StorefrontPageAnswer, v as StorefrontReviewsAnswer, f as ReviewEligibilityAnswer, a as ReviewSubmitInput, R as ReviewSubmitAnswer } from './contract-types-D2-SSPM9.js';
|
|
2
3
|
|
|
3
4
|
type QueryPairs = [string, string][];
|
|
4
5
|
|
|
@@ -35,6 +36,13 @@ type CatalogClientOptions = {
|
|
|
35
36
|
* the header.
|
|
36
37
|
*/
|
|
37
38
|
keyInQuery?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* A preview token (from the dashboard's preview link). Settings and pages
|
|
41
|
+
* reads then send it as `X-Storefront-Preview` and answer the draft: they
|
|
42
|
+
* are never cached, here or in the `cache` store. A token that does not
|
|
43
|
+
* verify is a `401` `PREVIEW_EXPIRED`. Other routes never carry it.
|
|
44
|
+
*/
|
|
45
|
+
previewToken?: string;
|
|
38
46
|
};
|
|
39
47
|
/** Next.js's `fetch` cache options; the SDK never imports Next. */
|
|
40
48
|
type NextFetchOptions = {
|
|
@@ -56,6 +64,14 @@ type CallOptions = {
|
|
|
56
64
|
*/
|
|
57
65
|
next?: NextFetchOptions;
|
|
58
66
|
};
|
|
67
|
+
/** Options of the reads that answer settings (`config`, `settings`, `pages`). */
|
|
68
|
+
type SettingsCallOptions = CallOptions & {
|
|
69
|
+
/**
|
|
70
|
+
* The storefront generation the caller knows exists (a change notice names
|
|
71
|
+
* it). Above the one the edge holds, the answer is read fresh.
|
|
72
|
+
*/
|
|
73
|
+
minGeneration?: number;
|
|
74
|
+
};
|
|
59
75
|
type RequestSpec = {
|
|
60
76
|
method: 'GET' | 'POST';
|
|
61
77
|
/** Already encoded, starting with `/`. */
|
|
@@ -84,6 +100,8 @@ type RequestSpec = {
|
|
|
84
100
|
* options). A POST takes them in its body, from `context()`.
|
|
85
101
|
*/
|
|
86
102
|
context?: 'priced' | 'locale';
|
|
103
|
+
/** The route answers a draft to a preview token, so the client sends one. */
|
|
104
|
+
preview?: boolean;
|
|
87
105
|
};
|
|
88
106
|
|
|
89
107
|
/** A list sort the catalog knows. */
|
|
@@ -141,7 +159,7 @@ type ThrottleLine = {
|
|
|
141
159
|
/** Throttle's cart as a cart session answers it; other keys pass through. */
|
|
142
160
|
type ThrottleCart = {
|
|
143
161
|
cartId: string;
|
|
144
|
-
currency:
|
|
162
|
+
currency: CurrencyCode;
|
|
145
163
|
subtotal: number;
|
|
146
164
|
total: number;
|
|
147
165
|
unitCount: number;
|
|
@@ -198,6 +216,32 @@ type CartCheckout = {
|
|
|
198
216
|
};
|
|
199
217
|
/** Per-call options of a cart-session call. */
|
|
200
218
|
type SessionCallOptions = Pick<CallOptions, 'signal' | 'timeoutMs'>;
|
|
219
|
+
/**
|
|
220
|
+
* What an absorb reports besides the cart (MC2-R39): lines of the other cart
|
|
221
|
+
* that could not move (no price in this cart's currency, or a custom line in
|
|
222
|
+
* another currency), a discount code this cart's currency refuses, and lines
|
|
223
|
+
* whose prices are unconfirmed until the next refresh or checkout.
|
|
224
|
+
*/
|
|
225
|
+
type CartWarning = {
|
|
226
|
+
code: 'lines_removed';
|
|
227
|
+
lines: {
|
|
228
|
+
referenceId: string | null;
|
|
229
|
+
code: string;
|
|
230
|
+
}[];
|
|
231
|
+
message: string;
|
|
232
|
+
} | {
|
|
233
|
+
code: 'discount_dropped';
|
|
234
|
+
discountCode: string;
|
|
235
|
+
message: string;
|
|
236
|
+
} | {
|
|
237
|
+
code: 'lines_stale';
|
|
238
|
+
message: string;
|
|
239
|
+
};
|
|
240
|
+
/** `CartSession.absorb()`'s answer: this session's cart after the move. */
|
|
241
|
+
type CartAbsorb = {
|
|
242
|
+
cart: ThrottleCart;
|
|
243
|
+
warnings: CartWarning[];
|
|
244
|
+
};
|
|
201
245
|
/** One Throttle cart session, addressed by its opaque id. */
|
|
202
246
|
type CartSession = {
|
|
203
247
|
readonly id: string;
|
|
@@ -212,6 +256,15 @@ type CartSession = {
|
|
|
212
256
|
refresh(call?: SessionCallOptions): Promise<CartSessionSnapshot>;
|
|
213
257
|
/** Refuses non-http(s) URLs before a request, and never returns a non-http(s) `checkoutUrl`. */
|
|
214
258
|
checkout(input: CartCheckoutInput, call?: SessionCallOptions): Promise<CartCheckout>;
|
|
259
|
+
/**
|
|
260
|
+
* Moves the cart of session `fromSessionId` into this session's cart, in
|
|
261
|
+
* this cart's currency, and closes the other one (`abandoned`, reason
|
|
262
|
+
* `merged`; a merged cart refuses writes with `409 cart_not_open`).
|
|
263
|
+
* Throttle never creates a cart or changes a currency or an owner here: a
|
|
264
|
+
* customer's cart only moves into that customer's cart (`403
|
|
265
|
+
* cart_owner_mismatch`).
|
|
266
|
+
*/
|
|
267
|
+
absorb(fromSessionId: string, call?: SessionCallOptions): Promise<CartAbsorb>;
|
|
215
268
|
};
|
|
216
269
|
type ProductsNamespace = {
|
|
217
270
|
list(query?: ProductsQuery, call?: CallOptions): Promise<StorefrontListAnswer>;
|
|
@@ -273,6 +326,24 @@ type CartsNamespace = {
|
|
|
273
326
|
throttleBaseUrl?: string;
|
|
274
327
|
}): CartSession;
|
|
275
328
|
};
|
|
329
|
+
/** `settings()`: the storefront settings and what versions them. */
|
|
330
|
+
type StorefrontSettingsResult = {
|
|
331
|
+
settings: StorefrontSettings;
|
|
332
|
+
settingsVersion: number;
|
|
333
|
+
generation: number;
|
|
334
|
+
};
|
|
335
|
+
/**
|
|
336
|
+
* `client.settings()`. The document may later be assembled from more than one
|
|
337
|
+
* extension (site design and catalog display); callers always read it through
|
|
338
|
+
* here, so its source can move without a change in the store.
|
|
339
|
+
*/
|
|
340
|
+
type SettingsNamespace = (call?: SettingsCallOptions) => Promise<StorefrontSettingsResult>;
|
|
341
|
+
type PagesNamespace = {
|
|
342
|
+
/** The published pages, in the store's order. */
|
|
343
|
+
list(call?: SettingsCallOptions): Promise<StorefrontPagesAnswer>;
|
|
344
|
+
/** One page; an unknown handle is a `404` `PAGE_NOT_FOUND`. */
|
|
345
|
+
get(handle: string, call?: SettingsCallOptions): Promise<StorefrontPageAnswer>;
|
|
346
|
+
};
|
|
276
347
|
type CatalogNamespaces = {
|
|
277
348
|
readonly products: ProductsNamespace;
|
|
278
349
|
readonly filters: FiltersNamespace;
|
|
@@ -282,7 +353,9 @@ type CatalogNamespaces = {
|
|
|
282
353
|
readonly suggest: (q: string, opts?: {
|
|
283
354
|
limit?: number;
|
|
284
355
|
} & CallOptions) => Promise<StorefrontSuggestAnswer>;
|
|
285
|
-
readonly config: (call?:
|
|
356
|
+
readonly config: (call?: SettingsCallOptions) => Promise<StorefrontConfigAnswer>;
|
|
357
|
+
readonly settings: SettingsNamespace;
|
|
358
|
+
readonly pages: PagesNamespace;
|
|
286
359
|
readonly reviews: ReviewsNamespace;
|
|
287
360
|
readonly carts: CartsNamespace;
|
|
288
361
|
};
|
|
@@ -290,7 +363,7 @@ type CatalogNamespaces = {
|
|
|
290
363
|
type OpenCartRef = {
|
|
291
364
|
sessionId: string;
|
|
292
365
|
cartId: string;
|
|
293
|
-
currency:
|
|
366
|
+
currency: CurrencyCode;
|
|
294
367
|
};
|
|
295
368
|
/**
|
|
296
369
|
* Where a signed-in customer's cart is found (P8-R14). Structural: the SDK
|
|
@@ -299,9 +372,9 @@ type OpenCartRef = {
|
|
|
299
372
|
* `currency`, or `null`; `whenClaimSettled` resolves once the sign-in's
|
|
300
373
|
* guest-cart claim is done, so the merged lines can be read.
|
|
301
374
|
* Resuming needs a currency: the hook's `currency` option or the client's;
|
|
302
|
-
* without one `useCart` makes no lookup.
|
|
303
|
-
*
|
|
304
|
-
*
|
|
375
|
+
* without one `useCart` makes no lookup. A write to a cart another device
|
|
376
|
+
* merged is refused (`409 cart_not_open`, never reopened), and `useCart`
|
|
377
|
+
* then looks the customer's cart up.
|
|
305
378
|
*/
|
|
306
379
|
type CustomerCartSource = {
|
|
307
380
|
getOpenCart(input: {
|
|
@@ -310,4 +383,4 @@ type CustomerCartSource = {
|
|
|
310
383
|
whenClaimSettled(): Promise<void>;
|
|
311
384
|
};
|
|
312
385
|
|
|
313
|
-
export type { CustomerCartSource as C, FieldsOption as F, NextFetchOptions as N, OpenCartRef as O, ProductsQuery as P, Redirect as R, SessionCallOptions as S, ThrottleCart as T, CategoryTreeDepth as a,
|
|
386
|
+
export type { TagsNamespace as A, ThrottleLine as B, CustomerCartSource as C, RequestSpec as D, FieldsOption as F, NextFetchOptions as N, OpenCartRef as O, ProductsQuery as P, Redirect as R, SessionCallOptions as S, ThrottleCart as T, CategoryTreeDepth as a, CartWarning as b, CallOptions as c, CartAbsorb as d, CartCheckout as e, CartCheckoutInput as f, CartSession as g, CartSessionBranding as h, CartSessionDetail as i, CartSessionSnapshot as j, CartsNamespace as k, CatalogClientOptions as l, CatalogHttpCache as m, CatalogHttpCacheEntry as n, CatalogNamespaces as o, CategoriesNamespace as p, CollectionsNamespace as q, CustomerTokenSource as r, FiltersNamespace as s, PagesNamespace as t, ProductsNamespace as u, ReviewsNamespace as v, SettingsCallOptions as w, SettingsNamespace as x, StorefrontSettingsResult as y, StorefrontSort as z };
|
package/dist/url.d.cts
CHANGED
package/dist/url.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usethrottle/catalog",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.0",
|
|
4
4
|
"description": "Storefront SDK for the Throttle Product Catalog: a typed client, React hooks, server helpers and URL helpers.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -56,6 +56,16 @@
|
|
|
56
56
|
"default": "./dist/server.cjs"
|
|
57
57
|
}
|
|
58
58
|
},
|
|
59
|
+
"./next": {
|
|
60
|
+
"import": {
|
|
61
|
+
"types": "./dist/next.d.ts",
|
|
62
|
+
"default": "./dist/next.js"
|
|
63
|
+
},
|
|
64
|
+
"require": {
|
|
65
|
+
"types": "./dist/next.d.cts",
|
|
66
|
+
"default": "./dist/next.cjs"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
59
69
|
"./url": {
|
|
60
70
|
"import": {
|
|
61
71
|
"types": "./dist/url.d.ts",
|
|
@@ -88,12 +98,17 @@
|
|
|
88
98
|
"access": "public"
|
|
89
99
|
},
|
|
90
100
|
"dependencies": {
|
|
91
|
-
"@usethrottle/errors": "^1.1.0"
|
|
101
|
+
"@usethrottle/errors": "^1.1.0",
|
|
102
|
+
"@usethrottle/money": "^1.0.0"
|
|
92
103
|
},
|
|
93
104
|
"peerDependencies": {
|
|
105
|
+
"next": ">=14",
|
|
94
106
|
"react": "^18.2.0 || ^19.0.0"
|
|
95
107
|
},
|
|
96
108
|
"peerDependenciesMeta": {
|
|
109
|
+
"next": {
|
|
110
|
+
"optional": true
|
|
111
|
+
},
|
|
97
112
|
"react": {
|
|
98
113
|
"optional": true
|
|
99
114
|
}
|