|
7 | 7 | type RenderIntegrationUI,
|
8 | 8 | } from '@gitbook/api';
|
9 | 9 | import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags';
|
| 10 | +import { parse as parseCacheControl } from '@tusbar/cache-control'; |
10 | 11 | import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache';
|
11 | 12 | import { cache } from '../cache';
|
12 | 13 | import { DataFetcherError, wrapCacheDataFetcherError } from './errors';
|
@@ -146,6 +147,43 @@ export function createDataFetcher(
|
146 | 147 | };
|
147 | 148 | }
|
148 | 149 |
|
| 150 | +/** |
| 151 | + * Infer the cache life from the api response headers. |
| 152 | + * @param response The response from the API call. |
| 153 | + * @param defaultCacheLife The default cache life to use if not specified in the response. |
| 154 | + * @returns nothing |
| 155 | + */ |
| 156 | +function cacheLifeFromResponse( |
| 157 | + response: HttpResponse<unknown, unknown>, |
| 158 | + defaultCacheLife: 'days' | 'max' | 'hours' | 'minutes' | 'seconds' |
| 159 | +) { |
| 160 | + const cacheControlHeader = response.headers.get('x-gitbook-cache-control'); |
| 161 | + const parsed = parseCacheControl(cacheControlHeader || ''); |
| 162 | + const maxAge = parsed?.maxAge ?? parsed?.sharedMaxAge; |
| 163 | + if (maxAge) { |
| 164 | + return cacheLife({ |
| 165 | + stale: 60 * 5, // This one is only for the client, |
| 166 | + revalidate: maxAge, // revalidate and expire are the same, we don't want stale data here |
| 167 | + expire: maxAge, |
| 168 | + }); |
| 169 | + } |
| 170 | + // Typings in Next is "wrong" and does not allow us just use it as `cacheLife(defaultCacheLife)` |
| 171 | + switch (defaultCacheLife) { |
| 172 | + case 'days': |
| 173 | + return cacheLife('days'); |
| 174 | + case 'max': |
| 175 | + return cacheLife('max'); |
| 176 | + case 'hours': |
| 177 | + return cacheLife('hours'); |
| 178 | + case 'minutes': |
| 179 | + return cacheLife('minutes'); |
| 180 | + case 'seconds': |
| 181 | + return cacheLife('seconds'); |
| 182 | + default: |
| 183 | + throw new Error(`Unknown default cache life: ${defaultCacheLife}`); |
| 184 | + } |
| 185 | +} |
| 186 | + |
149 | 187 | const getUserById = cache(async (input: DataFetcherInput, params: { userId: string }) => {
|
150 | 188 | 'use cache';
|
151 | 189 | return wrapCacheDataFetcherError(async () => {
|
@@ -307,7 +345,7 @@ const getRevisionPageDocument = cache(
|
307 | 345 | );
|
308 | 346 |
|
309 | 347 | cacheTag(...getCacheTagsFromResponse(res));
|
310 |
| - cacheLife('max'); |
| 348 | + cacheLifeFromResponse(res, 'max'); |
311 | 349 |
|
312 | 350 | return res.data;
|
313 | 351 | }
|
@@ -361,7 +399,7 @@ const getDocument = cache(
|
361 | 399 | }
|
362 | 400 | );
|
363 | 401 | cacheTag(...getCacheTagsFromResponse(res));
|
364 |
| - cacheLife('max'); |
| 402 | + cacheLifeFromResponse(res, 'max'); |
365 | 403 | return res.data;
|
366 | 404 | });
|
367 | 405 | });
|
@@ -406,7 +444,7 @@ const getComputedDocument = cache(
|
406 | 444 | }
|
407 | 445 | );
|
408 | 446 | cacheTag(...getCacheTagsFromResponse(res));
|
409 |
| - cacheLife('max'); |
| 447 | + cacheLifeFromResponse(res, 'max'); |
410 | 448 | return res.data;
|
411 | 449 | }
|
412 | 450 | );
|
|
0 commit comments