Skip to content

Commit 042a934

Browse files
committed
Merge branch 'main' into ai-page-journeys
2 parents be4ad7b + ae43ca1 commit 042a934

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+739
-493
lines changed

.changeset/chilly-snakes-sell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Cache AI Page Link summary

.changeset/cold-crews-drive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Tweak footer design (and refactor)

.changeset/fresh-peaches-smoke.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook-v2": patch
3+
---
4+
5+
Fix incoming URL for requests that were proxied

.changeset/hungry-candles-sing.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Add superscript and subscript text rendering

.changeset/lucky-elephants-hang.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Add support for buttons to GitBook.

.changeset/quick-countries-allow.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"gitbook": patch
3+
---
4+
5+
Fix ogimage generation crashing when site is using a custom WOFF2 font

.changeset/stupid-hats-reflect.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@gitbook/react-openapi': patch
3+
---
4+
5+
Handle optional security headers

.changeset/tame-camels-sparkle.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gitbook': patch
3+
---
4+
5+
Override Scalar's overscroll-behavior

.github/workflows/deploy-preview.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010
deploy-v1-cloudflare:
1111
name: Deploy v1 to Cloudflare Pages
1212
runs-on: ubuntu-latest
13-
environment:
13+
environment:
1414
name: ${{ github.ref == 'refs/heads/main' && '1c-production' || '1c-preview' }}
1515
url: ${{ steps.deploy.outputs.deployment-url }}
1616
permissions:
@@ -57,7 +57,7 @@ jobs:
5757
deploy-v2-vercel:
5858
name: Deploy v2 to Vercel (preview)
5959
runs-on: ubuntu-latest
60-
environment:
60+
environment:
6161
name: 2v-preview
6262
url: ${{ steps.deploy.outputs.deployment-url }}
6363
outputs:
@@ -78,7 +78,7 @@ jobs:
7878
deploy-v2-cloudflare:
7979
name: Deploy v2 to Cloudflare Worker (preview)
8080
runs-on: ubuntu-latest
81-
environment:
81+
environment:
8282
name: 2c-preview
8383
url: ${{ steps.deploy.outputs.deployment-url }}
8484
outputs:

bun.lock

Lines changed: 73 additions & 32 deletions
Large diffs are not rendered by default.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,15 @@
44
"devDependencies": {
55
"@biomejs/biome": "^1.9.4",
66
"@changesets/cli": "^2.27.12",
7-
"turbo": "^2.4.4",
7+
"turbo": "^2.5.0",
88
"vercel": "^39.3.0"
99
},
1010
"packageManager": "bun@1.2.8",
1111
"overrides": {
1212
"@codemirror/state": "6.4.1",
1313
"react": "18.3.1",
1414
"react-dom": "18.3.1",
15-
"@gitbook/api": "0.108.0"
15+
"@gitbook/api": "0.109.0"
1616
},
1717
"private": true,
1818
"scripts": {

packages/cache-do/turbo.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": ["//"],
3+
"tasks": {
4+
"generate": {
5+
"outputs": ["worker-configuration.d.ts"]
6+
}
7+
}
8+
}

packages/gitbook-v2/open-next.config.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import { defineCloudflareConfig } from '@opennextjs/cloudflare';
2-
import kvIncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/kv-incremental-cache';
2+
import r2IncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cache/r2-incremental-cache';
3+
import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache';
34
import doQueue from '@opennextjs/cloudflare/overrides/queue/do-queue';
45
import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache';
56

67
export default defineCloudflareConfig({
7-
incrementalCache: kvIncrementalCache,
8+
incrementalCache: withRegionalCache(r2IncrementalCache, { mode: 'long-lived' }),
89
tagCache: doShardedTagCache({
910
baseShardSize: 12,
1011
regionalCache: true,

packages/gitbook-v2/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
},
1717
"devDependencies": {
1818
"gitbook": "*",
19-
"@opennextjs/cloudflare": "^1.0.0-beta.0",
19+
"@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@7b3b305",
2020
"@types/rison": "^0.0.9",
2121
"tailwindcss": "^3.4.0",
2222
"postcss": "^8"

packages/gitbook-v2/src/lib/data/api.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ const getRevisionPageByPath = withCacheKey(
531531
) => {
532532
const uncached = unstable_cache(
533533
async () => getRevisionPageByPathUncached(cacheKey, input, params),
534-
[cacheKey],
534+
[cacheKey, 'v2'],
535535
{
536536
revalidate: RevalidationProfile.max,
537537
tags: [],
@@ -551,12 +551,13 @@ const getRevisionPageByPathUncached = withoutConcurrentExecution(
551551
return trace(
552552
`getRevisionPageByPath.uncached(${params.spaceId}, ${params.revisionId}, ${params.path})`,
553553
async () => {
554+
const encodedPath = encodeURIComponent(params.path);
554555
return wrapDataFetcherError(async () => {
555556
const api = apiClient(input);
556557
const res = await api.spaces.getPageInRevisionByPath(
557558
params.spaceId,
558559
params.revisionId,
559-
params.path,
560+
encodedPath,
560561
{}
561562
);
562563
return res.data;

packages/gitbook-v2/src/lib/data/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@ export * from './pages';
44
export * from './urls';
55
export * from './errors';
66
export * from './lookup';
7+
export * from './proxy';
8+
export * from './visitor';
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { describe, expect, it } from 'bun:test';
2+
import { getProxyRequestIdentifier, isProxyRequest } from './proxy';
3+
4+
describe('isProxyRequest', () => {
5+
it('should return true for proxy requests', () => {
6+
const proxyRequestURL = new URL('https://proxy.gitbook.site/sites/site_foo/hello/world');
7+
expect(isProxyRequest(proxyRequestURL)).toBe(true);
8+
});
9+
10+
it('should return false for non-proxy requests', () => {
11+
const nonProxyRequestURL = new URL('https://example.com/docs/foo/hello/world');
12+
expect(isProxyRequest(nonProxyRequestURL)).toBe(false);
13+
});
14+
});
15+
16+
describe('getProxyRequestIdentifier', () => {
17+
it('should return the correct identifier for proxy requests', () => {
18+
const proxyRequestURL = new URL('https://proxy.gitbook.site/sites/site_foo/hello/world');
19+
expect(getProxyRequestIdentifier(proxyRequestURL)).toBe('sites/site_foo');
20+
});
21+
});
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/**
2+
* Check if the request to the site was through a proxy.
3+
*/
4+
export function isProxyRequest(requestURL: URL): boolean {
5+
return (
6+
requestURL.host === 'proxy.gitbook.site' || requestURL.host === 'proxy.gitbook-staging.site'
7+
);
8+
}
9+
10+
export function getProxyRequestIdentifier(requestURL: URL): string {
11+
// For proxy requests, we extract the site ID from the pathname
12+
// e.g. https://proxy.gitbook.site/site/siteId/...
13+
const pathname = requestURL.pathname.slice(1).split('/');
14+
return pathname.slice(0, 2).join('/');
15+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { describe, expect, it } from 'bun:test';
2+
import { getVisitorAuthBasePath } from './visitor';
3+
4+
describe('getVisitorAuthBasePath', () => {
5+
it('should return the correct base path for proxy requests', () => {
6+
expect(
7+
getVisitorAuthBasePath(
8+
new URL('https://proxy.gitbook.site/sites/site_foo/hello/world'),
9+
{
10+
site: 'site_foo',
11+
siteSpace: 'sitesp_foo',
12+
basePath: '/foo',
13+
siteBasePath: '/foo',
14+
organization: 'org_foo',
15+
space: 'space_foo',
16+
pathname: '/hello/world',
17+
complete: false,
18+
apiToken: 'api_token_foo',
19+
canonicalUrl: 'https://example.com/docs/foo/hello/world',
20+
}
21+
)
22+
).toBe('/sites/site_foo/');
23+
});
24+
25+
it('should return the correct base path for non-proxy requests', () => {
26+
expect(
27+
getVisitorAuthBasePath(new URL('https://example.com/docs/foo/hello/world'), {
28+
site: 'site_foo',
29+
siteSpace: 'sitesp_foo',
30+
basePath: '/foo/',
31+
siteBasePath: '/foo/',
32+
organization: 'org_foo',
33+
space: 'space_foo',
34+
pathname: '/hello/world',
35+
complete: false,
36+
apiToken: 'api_token_foo',
37+
canonicalUrl: 'https://example.com/docs/foo/hello/world',
38+
})
39+
).toBe('/foo/');
40+
});
41+
});
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { withLeadingSlash, withTrailingSlash } from '@/lib/paths';
2+
import type { PublishedSiteContent } from '@gitbook/api';
3+
import { getProxyRequestIdentifier, isProxyRequest } from './proxy';
4+
5+
/**
6+
* Get the appropriate base path for the visitor authentication cookie.
7+
*/
8+
export function getVisitorAuthBasePath(
9+
siteRequestURL: URL,
10+
siteURLData: PublishedSiteContent
11+
): string {
12+
// The siteRequestURL for proxy requests is of the form `https://proxy.gitbook.com/site/siteId/...`
13+
// In such cases, we should not use the resolved siteBasePath for the cookie because for subsequent requests
14+
// we will not have the siteBasePath in the request URL in order to retrieve the cookie. So we use the
15+
// proxy identifier instead.
16+
return isProxyRequest(siteRequestURL)
17+
? withLeadingSlash(withTrailingSlash(getProxyRequestIdentifier(siteRequestURL)))
18+
: siteURLData.siteBasePath;
19+
}

packages/gitbook-v2/src/lib/images/getImageResizingContextId.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1+
import { getProxyRequestIdentifier, isProxyRequest } from '../data';
2+
13
/**
24
* Get the site identifier to use for image resizing for an incoming request.
35
* This identifier can be obtained before resolving the request URL.
46
*/
57
export function getImageResizingContextId(url: URL): string {
6-
if (url.host === 'proxy.gitbook.site' || url.host === 'proxy.gitbook-staging.site') {
7-
// For proxy requests, we extract the site ID from the pathname
8-
// e.g. https://proxy.gitbook.site/site/siteId/...
9-
const pathname = url.pathname.slice(1).split('/');
10-
return pathname.slice(0, 2).join('/');
8+
if (isProxyRequest(url)) {
9+
return getProxyRequestIdentifier(url);
1110
}
1211

1312
return url.host;

packages/gitbook-v2/src/middleware.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ import { serveResizedImage } from '@/routes/image';
1717
import {
1818
DataFetcherError,
1919
getPublishedContentByURL,
20+
getVisitorAuthBasePath,
2021
normalizeURL,
2122
throwIfDataError,
2223
} from '@v2/lib/data';
2324
import { isGitBookAssetsHostURL, isGitBookHostURL } from '@v2/lib/env';
2425
import { getImageResizingContextId } from '@v2/lib/images';
2526
import { MiddlewareHeaders } from '@v2/lib/middleware';
2627
import type { SiteURLData } from './lib/context';
27-
2828
export const config = {
2929
matcher: [
3030
'/((?!_next/static|_next/image|~gitbook/static|~gitbook/revalidate|~gitbook/monitoring|~scalar/proxy).*)',
@@ -137,24 +137,32 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
137137
return NextResponse.redirect(siteURLData.redirect);
138138
}
139139

140-
cookies.push(...getResponseCookiesForVisitorAuth(siteURLData.siteBasePath, visitorToken));
140+
cookies.push(
141+
...getResponseCookiesForVisitorAuth(
142+
getVisitorAuthBasePath(siteRequestURL, siteURLData),
143+
visitorToken
144+
)
145+
);
141146

142147
// We use the host/origin from the canonical URL to ensure the links are
143148
// correctly generated when the site is proxied. e.g. https://proxy.gitbook.com/site/siteId/...
144149
const siteCanonicalURL = new URL(siteURLData.canonicalUrl);
145150

151+
let incomingURL = requestURL;
152+
// For cases where the site is proxied, we use the canonical URL
153+
// as the incoming URL along with all the search params from the request.
154+
if (mode !== 'url') {
155+
incomingURL = siteCanonicalURL;
156+
incomingURL.search = requestURL.search;
157+
}
146158
//
147159
// Make sure the URL is clean of any va token after a successful lookup
148160
// The token is stored in a cookie that is set on the redirect response
149161
//
150-
const incomingURL = mode === 'url' ? requestURL : siteCanonicalURL;
151-
const requestURLWithoutToken = normalizeVisitorAuthURL(incomingURL);
152-
if (
153-
requestURLWithoutToken !== incomingURL &&
154-
requestURLWithoutToken.toString() !== incomingURL.toString()
155-
) {
162+
const incomingURLWithoutToken = normalizeVisitorAuthURL(incomingURL);
163+
if (incomingURLWithoutToken.toString() !== incomingURL.toString()) {
156164
return writeResponseCookies(
157-
NextResponse.redirect(requestURLWithoutToken.toString()),
165+
NextResponse.redirect(incomingURLWithoutToken.toString()),
158166
cookies
159167
);
160168
}

0 commit comments

Comments
 (0)