@usethrottle/catalog 1.3.0 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +22 -0
- package/README.md +105 -9
- package/dist/admin.cjs +16 -16
- package/dist/admin.d.cts +14 -2
- package/dist/admin.d.ts +14 -2
- package/dist/admin.js +1 -1
- package/dist/{catalog-error-BOaSKgY3.d.cts → catalog-error-bG1XHquN.d.cts} +1 -1
- package/dist/{catalog-error-CSoPS0-d.d.ts → catalog-error-c3BVgU6k.d.ts} +1 -1
- package/dist/{chunk-2YQNMBDI.cjs → chunk-2ODFFMZ4.cjs} +274 -38
- package/dist/chunk-5AJS3CX5.cjs +192 -0
- package/dist/{chunk-CADIHOIR.js → chunk-6K7H7HBD.js} +89 -2
- package/dist/chunk-P3ABGAF5.js +192 -0
- package/dist/{chunk-3DLH56OX.js → chunk-STNQNHNZ.js} +258 -22
- package/dist/{chunk-GU63WFXI.cjs → chunk-VOOX3WJY.cjs} +102 -15
- package/dist/{client-6j1Iz-iJ.d.cts → client-CGGlbacq.d.cts} +1 -1
- package/dist/{client-B3mL2-xJ.d.ts → client-CNsVr2lR.d.ts} +1 -1
- package/dist/{contract-types-BRyonOYH.d.cts → contract-types-BJMeJSTz.d.cts} +269 -2
- package/dist/{contract-types-BRyonOYH.d.ts → contract-types-BJMeJSTz.d.ts} +269 -2
- package/dist/index.cjs +12 -2
- package/dist/index.d.cts +53 -7
- package/dist/index.d.ts +53 -7
- package/dist/index.js +11 -1
- 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 +6 -4
- package/dist/react-extras.d.ts +6 -4
- package/dist/react-extras.js +20 -2
- package/dist/react.cjs +18 -14
- package/dist/react.d.cts +22 -5
- package/dist/react.d.ts +22 -5
- package/dist/react.js +6 -2
- package/dist/server.cjs +58 -66
- package/dist/server.d.cts +57 -10
- package/dist/server.d.ts +57 -10
- package/dist/server.js +52 -60
- package/dist/{types-D0PywKnN.d.cts → types-BtBxMbM5.d.cts} +40 -3
- package/dist/{types-BfveENg1.d.ts → types-dKvIfrVH.d.ts} +40 -3
- package/dist/url.d.cts +1 -1
- package/dist/url.d.ts +1 -1
- package/dist/{use-query-UWf6Pi5g.d.cts → use-query-C2rfTJd4.d.cts} +1 -1
- package/dist/{use-query-CR7r5tam.d.ts → use-query-DeCj1hXt.d.ts} +1 -1
- package/package.json +16 -2
package/dist/server.cjs
CHANGED
|
@@ -8,91 +8,79 @@
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
|
+
var _chunk5AJS3CX5cjs = require('./chunk-5AJS3CX5.cjs');
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
11
15
|
|
|
12
|
-
var _chunk2YQNMBDIcjs = require('./chunk-2YQNMBDI.cjs');
|
|
13
|
-
require('./chunk-ETV4XYOV.cjs');
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
function decode(segment) {
|
|
22
|
-
try {
|
|
23
|
-
return decodeURIComponent(segment);
|
|
24
|
-
} catch (e) {
|
|
25
|
-
return segment;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function tagsForUrl(url, storefront) {
|
|
29
|
-
const path = _nullishCoalesce(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
|
-
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
var _chunk2ODFFMZ4cjs = require('./chunk-2ODFFMZ4.cjs');
|
|
21
|
+
require('./chunk-ETV4XYOV.cjs');
|
|
64
22
|
|
|
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
|
-
const tags = rest.method === "GET" ? tagsForUrl(url, storefront) : null;
|
|
26
|
+
const tags = rest.method === "GET" ? _chunk5AJS3CX5cjs.tagsForUrl.call(void 0, 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 = _chunk5AJS3CX5cjs.cacheKindForPath.call(void 0, 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(_nullishCoalesce(cachePolicy, () => ( {}))))
|
|
49
|
+
if (policy !== void 0)
|
|
50
|
+
named.set(kind, _chunk5AJS3CX5cjs.policyToNext.call(void 0, policy));
|
|
51
|
+
const policies = (kind) => _nullishCoalesce(named.get(kind), () => ( (_optionalChain([next, 'optionalAccess', _ => _.revalidate]) === void 0 ? _chunk5AJS3CX5cjs.policyToNext.call(void 0, _chunk5AJS3CX5cjs.DEFAULT_CACHE_POLICY[kind]) : { revalidate: next.revalidate })));
|
|
52
|
+
const floorKey = _nullishCoalesce(namedFloorKey, () => ( rest.publishableKey));
|
|
82
53
|
const extraTags = _nullishCoalesce(_optionalChain([next, 'optionalAccess', _2 => _2.tags]), () => ( []));
|
|
83
|
-
const storefront = `${(_nullishCoalesce(rest.baseUrl, () => (
|
|
54
|
+
const storefront = `${(_nullishCoalesce(rest.baseUrl, () => ( _chunk2ODFFMZ4cjs.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) && _optionalChain([_chunk5AJS3CX5cjs.storefrontRoute.call(void 0, target.slice(storefront.length)), 'optionalAccess', _3 => _3.route]) === "config";
|
|
59
|
+
const floor = isConfig ? _chunk5AJS3CX5cjs.pendingMinGeneration.call(void 0, 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
|
+
_chunk5AJS3CX5cjs.observeGeneration.call(void 0,
|
|
75
|
+
(await response.clone().json()).generation,
|
|
76
|
+
floorKey
|
|
77
|
+
);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
}
|
|
80
|
+
return response;
|
|
81
|
+
});
|
|
94
82
|
};
|
|
95
|
-
return
|
|
83
|
+
return _chunk2ODFFMZ4cjs.createCatalogClient.call(void 0, {
|
|
96
84
|
...rest,
|
|
97
85
|
fetch: serverFetch
|
|
98
86
|
});
|
|
@@ -112,16 +100,16 @@ async function syncCatalogGeneration(input) {
|
|
|
112
100
|
|
|
113
101
|
// src/server/paging.ts
|
|
114
102
|
function bounds(opts) {
|
|
115
|
-
const pageSize = _nullishCoalesce(opts.pageSize, () => (
|
|
116
|
-
const max = _nullishCoalesce(opts.max, () => (
|
|
117
|
-
if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize >
|
|
103
|
+
const pageSize = _nullishCoalesce(opts.pageSize, () => ( _chunk2ODFFMZ4cjs.LIST_PAGE_SIZE));
|
|
104
|
+
const max = _nullishCoalesce(opts.max, () => ( _chunk2ODFFMZ4cjs.LIST_MAX_HANDLES));
|
|
105
|
+
if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize > _chunk2ODFFMZ4cjs.LIST_PAGE_SIZE)
|
|
118
106
|
throw invalid("pageSize must be a whole number from 1 to 100.");
|
|
119
107
|
if (!Number.isInteger(max) || max < 0)
|
|
120
108
|
throw invalid("max must be a whole number of 0 or more.");
|
|
121
109
|
return { pageSize, max };
|
|
122
110
|
}
|
|
123
111
|
function invalid(message) {
|
|
124
|
-
return new (0,
|
|
112
|
+
return new (0, _chunk2ODFFMZ4cjs.CatalogError)({
|
|
125
113
|
code: "INVALID_REQUEST",
|
|
126
114
|
message,
|
|
127
115
|
statusCode: 0,
|
|
@@ -191,7 +179,7 @@ function listAllCollectionHandles(client, opts = {}) {
|
|
|
191
179
|
}
|
|
192
180
|
async function listAllCategoryHandles(client, opts = {}) {
|
|
193
181
|
const { categories } = await client.categories.tree({
|
|
194
|
-
depth:
|
|
182
|
+
depth: _chunk2ODFFMZ4cjs.CATEGORY_TREE_MAX_DEPTH,
|
|
195
183
|
...call(opts.signal)
|
|
196
184
|
});
|
|
197
185
|
const out = [];
|
|
@@ -213,4 +201,8 @@ async function listAllCategoryHandles(client, opts = {}) {
|
|
|
213
201
|
|
|
214
202
|
|
|
215
203
|
|
|
216
|
-
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
exports.DEFAULT_CACHE_POLICY = _chunk5AJS3CX5cjs.DEFAULT_CACHE_POLICY; exports.SDK_VERSION = _chunk2ODFFMZ4cjs.SDK_VERSION; exports.cacheKindForPath = _chunk5AJS3CX5cjs.cacheKindForPath; exports.catalogTags = _chunk5AJS3CX5cjs.catalogTags; exports.createServerCatalogClient = createServerCatalogClient; exports.listAllCategoryHandles = listAllCategoryHandles; exports.listAllCollectionHandles = listAllCollectionHandles; exports.listAllProductHandles = listAllProductHandles; exports.policyToNext = _chunk5AJS3CX5cjs.policyToNext; exports.scopesToTags = _chunk5AJS3CX5cjs.scopesToTags; exports.staticProductParams = staticProductParams; exports.syncCatalogGeneration = syncCatalogGeneration;
|
package/dist/server.d.cts
CHANGED
|
@@ -2,11 +2,47 @@ export { S as SDK_VERSION } from './version-ev3p03v2.cjs';
|
|
|
2
2
|
import { CatalogClient } from './index.cjs';
|
|
3
3
|
import { CatalogClientOptions, NextFetchOptions } from './index.cjs';
|
|
4
4
|
import '@usethrottle/money';
|
|
5
|
-
import './contract-types-
|
|
5
|
+
import './contract-types-BJMeJSTz.cjs';
|
|
6
|
+
|
|
7
|
+
/** A kind of storefront data, each with its own cache policy. */
|
|
8
|
+
type CacheKind = 'settings' | 'navigation' | 'pages' | 'listings' | 'facets' | 'product' | 'related' | 'reviews' | 'search';
|
|
9
|
+
/**
|
|
10
|
+
* How long a read is cached: `'until-changed'` (an hour's safety TTL, cleared
|
|
11
|
+
* sooner by its cache tags), `'no-store'`, or a duration such as `'90s'` or
|
|
12
|
+
* `'5m'`.
|
|
13
|
+
*/
|
|
14
|
+
type CachePolicy = 'until-changed' | 'no-store' | `${number}s` | `${number}m`;
|
|
15
|
+
declare const DEFAULT_CACHE_POLICY: Readonly<Record<CacheKind, CachePolicy>>;
|
|
16
|
+
/** The kind of data a storefront path serves, or `null` when it is not a cacheable catalog read. */
|
|
17
|
+
declare function cacheKindForPath(path: string): CacheKind | null;
|
|
18
|
+
/**
|
|
19
|
+
* The `fetch` cache option a policy means: `{ revalidate }` for Next, or
|
|
20
|
+
* `'no-store'` (also for a zero duration). A string that is not a policy is
|
|
21
|
+
* `INVALID_REQUEST`.
|
|
22
|
+
*/
|
|
23
|
+
declare function policyToNext(policy: CachePolicy): {
|
|
24
|
+
revalidate: number | false;
|
|
25
|
+
} | 'no-store';
|
|
6
26
|
|
|
7
27
|
type ServerCatalogClientOptions = Omit<CatalogClientOptions, 'keyInQuery'> & {
|
|
8
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* `revalidate` replaces the default of every kind of read that
|
|
30
|
+
* `cachePolicy` does not name; `tags` are appended to the route's own.
|
|
31
|
+
*/
|
|
9
32
|
next?: NextFetchOptions;
|
|
33
|
+
/**
|
|
34
|
+
* How long each kind of read is cached, over the defaults (`settings`,
|
|
35
|
+
* `navigation`, `pages` and `product` `'until-changed'`, the rest `'60s'`).
|
|
36
|
+
* Navigation arrives inside `/config`, so its policy follows `settings`.
|
|
37
|
+
* A customer-token, cart or preview request is `no-store` regardless.
|
|
38
|
+
*/
|
|
39
|
+
cachePolicy?: Partial<Record<CacheKind, CachePolicy>>;
|
|
40
|
+
/**
|
|
41
|
+
* Names this store's generation floor (see `noteMinGeneration`). Defaults
|
|
42
|
+
* to `publishableKey`: one client per store needs nothing. Pass the same
|
|
43
|
+
* value to `createRevalidateHandler({ floorKey })`.
|
|
44
|
+
*/
|
|
45
|
+
floorKey?: string;
|
|
10
46
|
};
|
|
11
47
|
/**
|
|
12
48
|
* `createCatalogClient` for Next.js and other SSR: every catalog storefront
|
|
@@ -19,14 +55,25 @@ type ServerCatalogClientOptions = Omit<CatalogClientOptions, 'keyInQuery'> & {
|
|
|
19
55
|
declare function createServerCatalogClient(options: ServerCatalogClientOptions): CatalogClient;
|
|
20
56
|
|
|
21
57
|
/**
|
|
22
|
-
* The cache tags of one storefront route (P6-R17): always `throttle-catalog
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `
|
|
27
|
-
* `
|
|
58
|
+
* The cache tags of one storefront route (P6-R17): always `throttle-catalog`,
|
|
59
|
+
* the route's family (`throttle-catalog:config`, `:pages` or `:data`) and
|
|
60
|
+
* `throttle-catalog:<route>`, plus `throttle-catalog:<route>:<handle>` for a
|
|
61
|
+
* handle route. Next refuses a tag over 256 characters, so one is dropped.
|
|
62
|
+
* `route` is one of `config`, `products`, `filters`, `product`, `related`,
|
|
63
|
+
* `categories`, `category`, `tags`, `collections`, `collection`, `suggest`,
|
|
64
|
+
* `reviews`, `pages`, `page`.
|
|
28
65
|
*/
|
|
29
66
|
declare function catalogTags(route: string, handle?: string): string[];
|
|
67
|
+
/**
|
|
68
|
+
* The cache tags a storefront change notice clears, for the revalidate
|
|
69
|
+
* handler and any custom receiver. `settings` and `navigation` clear
|
|
70
|
+
* `throttle-catalog:config`; `filters` that and `:data`; `pages`
|
|
71
|
+
* `:pages`; `taxonomy` and `catalog` `:data`; `product:<h>`, `collection:<h>`
|
|
72
|
+
* and `category:<h>` their handle tag and `:data`; `reviews:<h>` the
|
|
73
|
+
* reviews and product handle tags. Anything else clears `:data`. In order,
|
|
74
|
+
* without repeats; a tag over 256 characters is dropped, as in `catalogTags`.
|
|
75
|
+
*/
|
|
76
|
+
declare function scopesToTags(scopes: readonly string[]): string[];
|
|
30
77
|
|
|
31
78
|
type SyncCatalogGenerationInput = {
|
|
32
79
|
client: CatalogClient;
|
|
@@ -77,5 +124,5 @@ declare function listAllCategoryHandles(client: CatalogClient, opts?: {
|
|
|
77
124
|
signal?: AbortSignal;
|
|
78
125
|
}): Promise<string[]>;
|
|
79
126
|
|
|
80
|
-
export { catalogTags, createServerCatalogClient, listAllCategoryHandles, listAllCollectionHandles, listAllProductHandles, staticProductParams, syncCatalogGeneration };
|
|
81
|
-
export type { ListHandlesOptions, ServerCatalogClientOptions, SyncCatalogGenerationInput };
|
|
127
|
+
export { DEFAULT_CACHE_POLICY, cacheKindForPath, catalogTags, createServerCatalogClient, listAllCategoryHandles, listAllCollectionHandles, listAllProductHandles, policyToNext, scopesToTags, staticProductParams, syncCatalogGeneration };
|
|
128
|
+
export type { CacheKind, CachePolicy, ListHandlesOptions, ServerCatalogClientOptions, SyncCatalogGenerationInput };
|
package/dist/server.d.ts
CHANGED
|
@@ -2,11 +2,47 @@ export { S as SDK_VERSION } from './version-ev3p03v2.js';
|
|
|
2
2
|
import { CatalogClient } from './index.js';
|
|
3
3
|
import { CatalogClientOptions, NextFetchOptions } from './index.js';
|
|
4
4
|
import '@usethrottle/money';
|
|
5
|
-
import './contract-types-
|
|
5
|
+
import './contract-types-BJMeJSTz.js';
|
|
6
|
+
|
|
7
|
+
/** A kind of storefront data, each with its own cache policy. */
|
|
8
|
+
type CacheKind = 'settings' | 'navigation' | 'pages' | 'listings' | 'facets' | 'product' | 'related' | 'reviews' | 'search';
|
|
9
|
+
/**
|
|
10
|
+
* How long a read is cached: `'until-changed'` (an hour's safety TTL, cleared
|
|
11
|
+
* sooner by its cache tags), `'no-store'`, or a duration such as `'90s'` or
|
|
12
|
+
* `'5m'`.
|
|
13
|
+
*/
|
|
14
|
+
type CachePolicy = 'until-changed' | 'no-store' | `${number}s` | `${number}m`;
|
|
15
|
+
declare const DEFAULT_CACHE_POLICY: Readonly<Record<CacheKind, CachePolicy>>;
|
|
16
|
+
/** The kind of data a storefront path serves, or `null` when it is not a cacheable catalog read. */
|
|
17
|
+
declare function cacheKindForPath(path: string): CacheKind | null;
|
|
18
|
+
/**
|
|
19
|
+
* The `fetch` cache option a policy means: `{ revalidate }` for Next, or
|
|
20
|
+
* `'no-store'` (also for a zero duration). A string that is not a policy is
|
|
21
|
+
* `INVALID_REQUEST`.
|
|
22
|
+
*/
|
|
23
|
+
declare function policyToNext(policy: CachePolicy): {
|
|
24
|
+
revalidate: number | false;
|
|
25
|
+
} | 'no-store';
|
|
6
26
|
|
|
7
27
|
type ServerCatalogClientOptions = Omit<CatalogClientOptions, 'keyInQuery'> & {
|
|
8
|
-
/**
|
|
28
|
+
/**
|
|
29
|
+
* `revalidate` replaces the default of every kind of read that
|
|
30
|
+
* `cachePolicy` does not name; `tags` are appended to the route's own.
|
|
31
|
+
*/
|
|
9
32
|
next?: NextFetchOptions;
|
|
33
|
+
/**
|
|
34
|
+
* How long each kind of read is cached, over the defaults (`settings`,
|
|
35
|
+
* `navigation`, `pages` and `product` `'until-changed'`, the rest `'60s'`).
|
|
36
|
+
* Navigation arrives inside `/config`, so its policy follows `settings`.
|
|
37
|
+
* A customer-token, cart or preview request is `no-store` regardless.
|
|
38
|
+
*/
|
|
39
|
+
cachePolicy?: Partial<Record<CacheKind, CachePolicy>>;
|
|
40
|
+
/**
|
|
41
|
+
* Names this store's generation floor (see `noteMinGeneration`). Defaults
|
|
42
|
+
* to `publishableKey`: one client per store needs nothing. Pass the same
|
|
43
|
+
* value to `createRevalidateHandler({ floorKey })`.
|
|
44
|
+
*/
|
|
45
|
+
floorKey?: string;
|
|
10
46
|
};
|
|
11
47
|
/**
|
|
12
48
|
* `createCatalogClient` for Next.js and other SSR: every catalog storefront
|
|
@@ -19,14 +55,25 @@ type ServerCatalogClientOptions = Omit<CatalogClientOptions, 'keyInQuery'> & {
|
|
|
19
55
|
declare function createServerCatalogClient(options: ServerCatalogClientOptions): CatalogClient;
|
|
20
56
|
|
|
21
57
|
/**
|
|
22
|
-
* The cache tags of one storefront route (P6-R17): always `throttle-catalog
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* `
|
|
27
|
-
* `
|
|
58
|
+
* The cache tags of one storefront route (P6-R17): always `throttle-catalog`,
|
|
59
|
+
* the route's family (`throttle-catalog:config`, `:pages` or `:data`) and
|
|
60
|
+
* `throttle-catalog:<route>`, plus `throttle-catalog:<route>:<handle>` for a
|
|
61
|
+
* handle route. Next refuses a tag over 256 characters, so one is dropped.
|
|
62
|
+
* `route` is one of `config`, `products`, `filters`, `product`, `related`,
|
|
63
|
+
* `categories`, `category`, `tags`, `collections`, `collection`, `suggest`,
|
|
64
|
+
* `reviews`, `pages`, `page`.
|
|
28
65
|
*/
|
|
29
66
|
declare function catalogTags(route: string, handle?: string): string[];
|
|
67
|
+
/**
|
|
68
|
+
* The cache tags a storefront change notice clears, for the revalidate
|
|
69
|
+
* handler and any custom receiver. `settings` and `navigation` clear
|
|
70
|
+
* `throttle-catalog:config`; `filters` that and `:data`; `pages`
|
|
71
|
+
* `:pages`; `taxonomy` and `catalog` `:data`; `product:<h>`, `collection:<h>`
|
|
72
|
+
* and `category:<h>` their handle tag and `:data`; `reviews:<h>` the
|
|
73
|
+
* reviews and product handle tags. Anything else clears `:data`. In order,
|
|
74
|
+
* without repeats; a tag over 256 characters is dropped, as in `catalogTags`.
|
|
75
|
+
*/
|
|
76
|
+
declare function scopesToTags(scopes: readonly string[]): string[];
|
|
30
77
|
|
|
31
78
|
type SyncCatalogGenerationInput = {
|
|
32
79
|
client: CatalogClient;
|
|
@@ -77,5 +124,5 @@ declare function listAllCategoryHandles(client: CatalogClient, opts?: {
|
|
|
77
124
|
signal?: AbortSignal;
|
|
78
125
|
}): Promise<string[]>;
|
|
79
126
|
|
|
80
|
-
export { catalogTags, createServerCatalogClient, listAllCategoryHandles, listAllCollectionHandles, listAllProductHandles, staticProductParams, syncCatalogGeneration };
|
|
81
|
-
export type { ListHandlesOptions, ServerCatalogClientOptions, SyncCatalogGenerationInput };
|
|
127
|
+
export { DEFAULT_CACHE_POLICY, cacheKindForPath, catalogTags, createServerCatalogClient, listAllCategoryHandles, listAllCollectionHandles, listAllProductHandles, policyToNext, scopesToTags, staticProductParams, syncCatalogGeneration };
|
|
128
|
+
export type { CacheKind, CachePolicy, ListHandlesOptions, ServerCatalogClientOptions, SyncCatalogGenerationInput };
|
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-P3ABGAF5.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-STNQNHNZ.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,5 +1,5 @@
|
|
|
1
1
|
import { CurrencyCode } from '@usethrottle/money';
|
|
2
|
-
import { C as CartCheckAnswer,
|
|
2
|
+
import { C as CartCheckAnswer, j as CartCreateRequest, i as CartCreateAnswer, d as StorefrontListAnswer, g as StorefrontProductAnswer, w as StorefrontRelatedAnswer, r as StorefrontFilterValuesAnswer, l as StorefrontCategoriesAnswer, m as StorefrontCategoryAnswer, z as StorefrontTagsAnswer, o as StorefrontCollectionsAnswer, n as StorefrontCollectionAnswer, y as StorefrontSuggestAnswer, p as StorefrontConfigAnswer, S as StorefrontSettings, v as StorefrontPagesAnswer, u as StorefrontPageAnswer, x as StorefrontReviewsAnswer, f as ReviewEligibilityAnswer, a as ReviewSubmitInput, R as ReviewSubmitAnswer } from './contract-types-BJMeJSTz.cjs';
|
|
3
3
|
|
|
4
4
|
type QueryPairs = [string, string][];
|
|
5
5
|
|
|
@@ -36,6 +36,13 @@ type CatalogClientOptions = {
|
|
|
36
36
|
* the header.
|
|
37
37
|
*/
|
|
38
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;
|
|
39
46
|
};
|
|
40
47
|
/** Next.js's `fetch` cache options; the SDK never imports Next. */
|
|
41
48
|
type NextFetchOptions = {
|
|
@@ -57,6 +64,14 @@ type CallOptions = {
|
|
|
57
64
|
*/
|
|
58
65
|
next?: NextFetchOptions;
|
|
59
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
|
+
};
|
|
60
75
|
type RequestSpec = {
|
|
61
76
|
method: 'GET' | 'POST';
|
|
62
77
|
/** Already encoded, starting with `/`. */
|
|
@@ -85,6 +100,8 @@ type RequestSpec = {
|
|
|
85
100
|
* options). A POST takes them in its body, from `context()`.
|
|
86
101
|
*/
|
|
87
102
|
context?: 'priced' | 'locale';
|
|
103
|
+
/** The route answers a draft to a preview token, so the client sends one. */
|
|
104
|
+
preview?: boolean;
|
|
88
105
|
};
|
|
89
106
|
|
|
90
107
|
/** A list sort the catalog knows. */
|
|
@@ -309,6 +326,24 @@ type CartsNamespace = {
|
|
|
309
326
|
throttleBaseUrl?: string;
|
|
310
327
|
}): CartSession;
|
|
311
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
|
+
};
|
|
312
347
|
type CatalogNamespaces = {
|
|
313
348
|
readonly products: ProductsNamespace;
|
|
314
349
|
readonly filters: FiltersNamespace;
|
|
@@ -318,7 +353,9 @@ type CatalogNamespaces = {
|
|
|
318
353
|
readonly suggest: (q: string, opts?: {
|
|
319
354
|
limit?: number;
|
|
320
355
|
} & CallOptions) => Promise<StorefrontSuggestAnswer>;
|
|
321
|
-
readonly config: (call?:
|
|
356
|
+
readonly config: (call?: SettingsCallOptions) => Promise<StorefrontConfigAnswer>;
|
|
357
|
+
readonly settings: SettingsNamespace;
|
|
358
|
+
readonly pages: PagesNamespace;
|
|
322
359
|
readonly reviews: ReviewsNamespace;
|
|
323
360
|
readonly carts: CartsNamespace;
|
|
324
361
|
};
|
|
@@ -346,4 +383,4 @@ type CustomerCartSource = {
|
|
|
346
383
|
whenClaimSettled(): Promise<void>;
|
|
347
384
|
};
|
|
348
385
|
|
|
349
|
-
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, 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,
|
|
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 };
|