Skip to content

Commit 12c9d76

Browse files
authored
Adapt OpenAPI to eval adaptive exprs & prefill TryIt config (#3607)
1 parent 4520728 commit 12c9d76

File tree

17 files changed

+788
-37
lines changed

17 files changed

+788
-37
lines changed

.changeset/clean-queens-train.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"@gitbook/react-openapi": minor
3+
"gitbook": minor
4+
"@gitbook/openapi-parser": patch
5+
---
6+
7+
Adapt OpenAPI blocks to eval adaptive exprs & prefill TryIt config

bun.lock

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
"@gitbook/cache-tags": "workspace:*",
100100
"@gitbook/colors": "workspace:*",
101101
"@gitbook/emoji-codepoints": "workspace:*",
102+
"@gitbook/expr": "workspace:*",
102103
"@gitbook/fonts": "workspace:*",
103104
"@gitbook/icons": "workspace:*",
104105
"@gitbook/openapi-parser": "workspace:*",
@@ -262,9 +263,11 @@
262263
"name": "@gitbook/react-openapi",
263264
"version": "1.3.6",
264265
"dependencies": {
266+
"@gitbook/expr": "workspace:*",
265267
"@gitbook/openapi-parser": "workspace:*",
266268
"@scalar/api-client-react": "^1.3.16",
267269
"@scalar/oas-utils": "^0.2.130",
270+
"@scalar/types": "^0.1.9",
268271
"clsx": "^2.1.1",
269272
"flatted": "^3.2.9",
270273
"js-yaml": "^4.1.0",
@@ -1173,7 +1176,7 @@
11731176

11741177
"@scalar/themes": ["@scalar/themes@0.9.86", "", { "dependencies": { "@scalar/types": "0.1.7" } }, "sha512-QUHo9g5oSWi+0Lm1vJY9TaMZRau8LHg+vte7q5BVTBnu6NuQfigCaN+ouQ73FqIVd96TwMO6Db+dilK1B+9row=="],
11751178

1176-
"@scalar/types": ["@scalar/types@0.2.3", "", { "dependencies": { "@scalar/openapi-types": "0.3.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA=="],
1179+
"@scalar/types": ["@scalar/types@0.1.16", "", { "dependencies": { "@scalar/openapi-types": "0.2.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-v1L96F8Inn27NUw5Xcl0iifw3SOX0R9WBV6GeswR23s8i1SJz7UiLEi8rLOqOB5DiLtC8hDK0kNl4TKn7MNJWQ=="],
11771180

11781181
"@scalar/use-codemirror": ["@scalar/use-codemirror@0.12.13", "", { "dependencies": { "@codemirror/autocomplete": "^6.18.3", "@codemirror/commands": "^6.7.1", "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.8", "@codemirror/lang-json": "^6.0.0", "@codemirror/lang-xml": "^6.0.0", "@codemirror/lang-yaml": "^6.1.2", "@codemirror/language": "^6.10.7", "@codemirror/lint": "^6.8.4", "@codemirror/state": "^6.5.0", "@codemirror/view": "^6.35.3", "@lezer/common": "^1.2.3", "@lezer/highlight": "^1.2.1", "@replit/codemirror-css-color-picker": "^6.3.0", "@scalar/components": "0.14.12", "codemirror": "^6.0.0", "vue": "^3.5.12" } }, "sha512-XOXCUT3b1l0gkp4CY51e7xMgR6IP/AMpYIZ97fBOOjnrUIJ8B/J+U/BMS6+lPKiN0hexaSALRWlVBf90txdT8w=="],
11791182

@@ -3963,10 +3966,14 @@
39633966

39643967
"@scalar/api-client/@scalar/themes": ["@scalar/themes@0.13.4", "", { "dependencies": { "@scalar/types": "0.2.3", "nanoid": "^5.1.5" } }, "sha512-PMiSqX+MZF29u32ogEjTVA5c/LBd5cE/QPF7lK0EJeemXRy2r/nWbvrXtqD9Y7Sw54g1caVNjK8yJcCYJdB6hw=="],
39653968

3969+
"@scalar/api-client/@scalar/types": ["@scalar/types@0.2.3", "", { "dependencies": { "@scalar/openapi-types": "0.3.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA=="],
3970+
39663971
"@scalar/api-client/pretty-ms": ["pretty-ms@8.0.0", "", { "dependencies": { "parse-ms": "^3.0.0" } }, "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q=="],
39673972

39683973
"@scalar/api-client/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
39693974

3975+
"@scalar/api-client-react/@scalar/types": ["@scalar/types@0.2.3", "", { "dependencies": { "@scalar/openapi-types": "0.3.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA=="],
3976+
39703977
"@scalar/components/@scalar/oas-utils": ["@scalar/oas-utils@0.4.7", "", { "dependencies": { "@hyperjump/browser": "^1.1.0", "@hyperjump/json-schema": "^1.9.6", "@scalar/helpers": "0.0.4", "@scalar/object-utils": "1.2.1", "@scalar/openapi-types": "0.3.3", "@scalar/themes": "0.13.4", "@scalar/types": "0.2.3", "@types/har-format": "^1.2.15", "flatted": "^3.3.1", "microdiff": "^1.4.0", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "yaml": "^2.4.5", "zod": "3.24.1" } }, "sha512-Vp8iZZCjNKXcPpL0Yr6dmy+VOSN1LZ5CdlGAHtp+pxuQTeahV6FxiVjMc9kdFve77fTNyut4seVAD6d7rDUDcA=="],
39713978

39723979
"@scalar/components/@scalar/themes": ["@scalar/themes@0.13.4", "", { "dependencies": { "@scalar/types": "0.2.3", "nanoid": "^5.1.5" } }, "sha512-PMiSqX+MZF29u32ogEjTVA5c/LBd5cE/QPF7lK0EJeemXRy2r/nWbvrXtqD9Y7Sw54g1caVNjK8yJcCYJdB6hw=="],
@@ -3983,7 +3990,7 @@
39833990

39843991
"@scalar/themes/@scalar/types": ["@scalar/types@0.1.7", "", { "dependencies": { "@scalar/openapi-types": "0.2.0", "@unhead/schema": "^1.11.11", "nanoid": "^5.1.5", "type-fest": "^4.20.0", "zod": "^3.23.8" } }, "sha512-irIDYzTQG2KLvFbuTI8k2Pz/R4JR+zUUSykVTbEMatkzMmVFnn1VzNSMlODbadycwZunbnL2tA27AXed9URVjw=="],
39853992

3986-
"@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.3.3", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-mr3OvbCyvuzSDgv9gR9D3pJAsl82BRNBp1P8ldgRgKF4Wiswfa7nQeKibpX0kHSGXLgiyvxWJULUFwgdWMXNNA=="],
3993+
"@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.2.3", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-O1GwqLpcRc3GKXTbeBZ5E12fXR2ltpqGWk4RfhoN4ebKZsPVknV5at5425G97E1SwMy12BporRvn90k1Z+MruQ=="],
39873994

39883995
"@scalar/types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
39893996

@@ -5081,18 +5088,28 @@
50815088

50825089
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
50835090

5091+
"@scalar/api-client-react/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.3.3", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-mr3OvbCyvuzSDgv9gR9D3pJAsl82BRNBp1P8ldgRgKF4Wiswfa7nQeKibpX0kHSGXLgiyvxWJULUFwgdWMXNNA=="],
5092+
5093+
"@scalar/api-client-react/@scalar/types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
5094+
50845095
"@scalar/api-client/pretty-ms/parse-ms": ["parse-ms@3.0.0", "", {}, "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw=="],
50855096

50865097
"@scalar/components/@scalar/oas-utils/@scalar/object-utils": ["@scalar/object-utils@1.2.1", "", { "dependencies": { "flatted": "^3.3.1", "just-clone": "^6.2.0", "ts-deepmerge": "^7.0.1", "type-fest": "^4.20.0" } }, "sha512-2P/0In6XSrV1Ye8yU3LyOWJKWkzXpxf0thHmTfJriqGBZz41s06td2KKqoqHSEEi+FAsl3O1ngmOOzLM3QUkkw=="],
50875098

50885099
"@scalar/components/@scalar/oas-utils/@scalar/openapi-types": ["@scalar/openapi-types@0.3.3", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-mr3OvbCyvuzSDgv9gR9D3pJAsl82BRNBp1P8ldgRgKF4Wiswfa7nQeKibpX0kHSGXLgiyvxWJULUFwgdWMXNNA=="],
50895100

5101+
"@scalar/components/@scalar/oas-utils/@scalar/types": ["@scalar/types@0.2.3", "", { "dependencies": { "@scalar/openapi-types": "0.3.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA=="],
5102+
50905103
"@scalar/components/@scalar/oas-utils/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
50915104

5105+
"@scalar/components/@scalar/themes/@scalar/types": ["@scalar/types@0.2.3", "", { "dependencies": { "@scalar/openapi-types": "0.3.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA=="],
5106+
50925107
"@scalar/postman-to-openapi/@scalar/oas-utils/@scalar/object-utils": ["@scalar/object-utils@1.2.1", "", { "dependencies": { "flatted": "^3.3.1", "just-clone": "^6.2.0", "ts-deepmerge": "^7.0.1", "type-fest": "^4.20.0" } }, "sha512-2P/0In6XSrV1Ye8yU3LyOWJKWkzXpxf0thHmTfJriqGBZz41s06td2KKqoqHSEEi+FAsl3O1ngmOOzLM3QUkkw=="],
50935108

50945109
"@scalar/postman-to-openapi/@scalar/oas-utils/@scalar/themes": ["@scalar/themes@0.13.4", "", { "dependencies": { "@scalar/types": "0.2.3", "nanoid": "^5.1.5" } }, "sha512-PMiSqX+MZF29u32ogEjTVA5c/LBd5cE/QPF7lK0EJeemXRy2r/nWbvrXtqD9Y7Sw54g1caVNjK8yJcCYJdB6hw=="],
50955110

5111+
"@scalar/postman-to-openapi/@scalar/oas-utils/@scalar/types": ["@scalar/types@0.2.3", "", { "dependencies": { "@scalar/openapi-types": "0.3.3", "nanoid": "^5.1.5", "zod": "3.24.1" } }, "sha512-K1/Vp5xaQ8TEGDYwHg88OBuQWJRWrE3ToZ0Z2LJwskcY1Eg+uDiRQXKF4QPngZzu5rrWgRErAq4yte4Y7omqgA=="],
5112+
50965113
"@scalar/postman-to-openapi/@scalar/oas-utils/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
50975114

50985115
"@scalar/postman-to-openapi/@scalar/openapi-types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
@@ -5439,6 +5456,10 @@
54395456

54405457
"@radix-ui/react-visually-hidden/@radix-ui/react-primitive/@radix-ui/react-slot/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
54415458

5459+
"@scalar/components/@scalar/themes/@scalar/types/@scalar/openapi-types": ["@scalar/openapi-types@0.3.3", "", { "dependencies": { "zod": "3.24.1" } }, "sha512-mr3OvbCyvuzSDgv9gR9D3pJAsl82BRNBp1P8ldgRgKF4Wiswfa7nQeKibpX0kHSGXLgiyvxWJULUFwgdWMXNNA=="],
5460+
5461+
"@scalar/components/@scalar/themes/@scalar/types/zod": ["zod@3.24.1", "", {}, "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A=="],
5462+
54425463
"@smithy/core/@smithy/util-stream/@smithy/fetch-http-handler/@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wU87iWZoCbcqrwszsOewEIuq+SU2mSoBE2CcsLwE0I19m0B2gOJr1MVjxWcDQYOzHbR1xCk7AcOBbGFUYOKvdg=="],
54435464

54445465
"@smithy/core/@smithy/util-stream/@smithy/node-http-handler/@smithy/abort-controller": ["@smithy/abort-controller@4.0.1", "", { "dependencies": { "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-fiUIYgIgRjMWznk6iLJz35K2YxSLHzLBA/RC6lBrKfQ8fHbPfvk7Pk9UvpKoHgJjI18MnbPuEju53zcVy6KF1g=="],

packages/gitbook/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"@gitbook/browser-types": "workspace:*",
88
"@gitbook/cache-tags": "workspace:*",
99
"@gitbook/colors": "workspace:*",
10+
"@gitbook/expr": "workspace:*",
1011
"@gitbook/emoji-codepoints": "workspace:*",
1112
"@gitbook/fonts": "workspace:*",
1213
"@gitbook/icons": "workspace:*",
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
'use client';
2+
3+
import type { GitBookSiteContext } from '@/lib/context';
4+
import { OpenAPIPrefillContextProvider } from '@gitbook/react-openapi';
5+
import * as React from 'react';
6+
import { createContext, useContext } from 'react';
7+
8+
export type AdaptiveVisitorClaimsData = {
9+
visitor: {
10+
claims: Record<string, unknown> & { unsigned: Record<string, unknown> };
11+
};
12+
};
13+
14+
/**
15+
* In-memory cache of visitor claim readers keyed by contextId.
16+
*/
17+
const adaptiveVisitorReaderCache = new Map<
18+
string,
19+
ReturnType<typeof createResourceReader<AdaptiveVisitorClaimsData | null>>
20+
>();
21+
22+
function createResourceReader<T>(promise: Promise<T>) {
23+
let result: T | null | undefined;
24+
25+
const suspender = (async () => {
26+
try {
27+
result = await promise;
28+
} catch {
29+
result = null;
30+
}
31+
})();
32+
33+
return {
34+
read() {
35+
if (result === undefined) {
36+
throw suspender;
37+
}
38+
return result;
39+
},
40+
};
41+
}
42+
43+
/**
44+
* Return an adaptive visitor claims cached reader for a given endpoint URL and contextId.
45+
*/
46+
function getAdaptiveVisitorClaimsReader(url: string, contextId: string) {
47+
let reader = adaptiveVisitorReaderCache.get(contextId);
48+
if (!reader) {
49+
const promise = (async () => {
50+
try {
51+
const res = await fetch(url);
52+
if (!res.ok) {
53+
return null;
54+
}
55+
return await res.json<AdaptiveVisitorClaimsData>();
56+
} catch {
57+
return null;
58+
}
59+
})();
60+
61+
reader = createResourceReader(promise);
62+
adaptiveVisitorReaderCache.set(contextId, reader);
63+
}
64+
return reader;
65+
}
66+
67+
export type AdaptiveVisitorContextValue = () => AdaptiveVisitorClaimsData | null;
68+
69+
const AdaptiveVisitorContext = createContext<AdaptiveVisitorContextValue>(() => null);
70+
71+
/**
72+
* Provide context to adapt site based on visitor claims.
73+
*/
74+
export function AdaptiveVisitorContextProvider(
75+
props: React.PropsWithChildren<{
76+
visitorClaimsURL: string;
77+
contextId: GitBookSiteContext['contextId'] | undefined;
78+
}>
79+
) {
80+
const { visitorClaimsURL, contextId, children } = props;
81+
82+
const getAdaptiveVisitorClaims = React.useCallback(() => {
83+
if (!contextId) {
84+
return null;
85+
}
86+
return getAdaptiveVisitorClaimsReader(visitorClaimsURL, contextId).read();
87+
}, [visitorClaimsURL, contextId]);
88+
89+
return (
90+
<AdaptiveVisitorContext.Provider value={getAdaptiveVisitorClaims}>
91+
<OpenAPIPrefillContextProvider getPrefillInputContextData={getAdaptiveVisitorClaims}>
92+
{children}
93+
</OpenAPIPrefillContextProvider>
94+
</AdaptiveVisitorContext.Provider>
95+
);
96+
}
97+
98+
/**
99+
* Hook that returns a suspensable getter for adaptive visitor claims data.
100+
*/
101+
export function useAdaptiveVisitor(): AdaptiveVisitorContextValue {
102+
return useContext(AdaptiveVisitorContext);
103+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './AdaptiveVisitorContextProvider';

packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import { GITBOOK_APP_URL } from '@/lib/env';
1717
import { AIChatProvider } from '../AI';
1818
import type { RenderAIMessageOptions } from '../AI';
1919
import { AIChat } from '../AIChat';
20+
import { AdaptiveVisitorContextProvider } from '../Adaptive';
2021
import { Announcement } from '../Announcement';
2122
import { SpacesDropdown } from '../Header/SpacesDropdown';
2223
import { InsightsProvider } from '../Insights';
@@ -56,29 +57,38 @@ export function SpaceLayoutServerContext(props: SpaceLayoutProps) {
5657
eventUrl.searchParams.set('o', context.organizationId);
5758
eventUrl.searchParams.set('s', context.site.id);
5859

60+
const getVisitorClaimsUrl = context.linker.toAbsoluteURL(
61+
context.linker.toPathInSite('/~gitbook/visitor')
62+
);
63+
5964
return (
6065
<SpaceLayoutContextProvider basePath={context.linker.toPathInSpace('')}>
61-
<CurrentContentProvider
62-
organizationId={context.organizationId}
63-
siteId={context.site.id}
64-
siteSectionId={context.sections?.current?.id ?? null}
65-
siteSpaceId={context.siteSpace.id}
66-
siteShareKey={context.shareKey ?? null}
67-
spaceId={context.space.id}
68-
revisionId={context.revisionId}
69-
visitorAuthClaims={visitorAuthClaims}
66+
<AdaptiveVisitorContextProvider
67+
contextId={context.contextId}
68+
visitorClaimsURL={getVisitorClaimsUrl}
7069
>
71-
<InsightsProvider
72-
enabled={withTracking}
73-
appURL={GITBOOK_APP_URL}
74-
eventUrl={eventUrl.toString()}
75-
visitorCookieTrackingEnabled={customization.insights?.trackingCookie}
70+
<CurrentContentProvider
71+
organizationId={context.organizationId}
72+
siteId={context.site.id}
73+
siteSectionId={context.sections?.current?.id ?? null}
74+
siteSpaceId={context.siteSpace.id}
75+
siteShareKey={context.shareKey ?? null}
76+
spaceId={context.space.id}
77+
revisionId={context.revisionId}
78+
visitorAuthClaims={visitorAuthClaims}
7679
>
77-
<AIChatProvider renderMessageOptions={aiChatRenderMessageOptions}>
78-
{children}
79-
</AIChatProvider>
80-
</InsightsProvider>
81-
</CurrentContentProvider>
80+
<InsightsProvider
81+
enabled={withTracking}
82+
appURL={GITBOOK_APP_URL}
83+
eventUrl={eventUrl.toString()}
84+
visitorCookieTrackingEnabled={customization.insights?.trackingCookie}
85+
>
86+
<AIChatProvider renderMessageOptions={aiChatRenderMessageOptions}>
87+
{children}
88+
</AIChatProvider>
89+
</InsightsProvider>
90+
</CurrentContentProvider>
91+
</AdaptiveVisitorContextProvider>
8292
</SpaceLayoutContextProvider>
8393
);
8494
}

packages/gitbook/src/lib/visitors.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { type JwtPayload, jwtDecode } from 'jwt-decode';
2-
import type { NextRequest } from 'next/server';
2+
import { type NextRequest, NextResponse } from 'next/server';
33
import hash from 'object-hash';
44

55
const VISITOR_AUTH_PARAM = 'jwt_token';
@@ -443,3 +443,45 @@ export function getVisitorAuthCookieMaxAge(decoded: JwtPayload): number {
443443

444444
return defaultMaxAge;
445445
}
446+
447+
/**
448+
* Handler for the /~gitbook/visitor middleware route to expose visitor data.
449+
*/
450+
export function serveVisitorClaimsDataRequest(request: NextRequest, siteRequestURL: URL) {
451+
const { visitorToken, unsignedClaims } = getVisitorData({
452+
cookies: request.cookies.getAll(),
453+
url: siteRequestURL,
454+
});
455+
456+
if (!visitorToken && !Object.keys(unsignedClaims).length) {
457+
return NextResponse.json({});
458+
}
459+
460+
const visitorClaims = {
461+
visitor: {
462+
claims: {
463+
unsigned: unsignedClaims,
464+
},
465+
},
466+
};
467+
468+
if (!visitorToken) {
469+
return NextResponse.json(visitorClaims);
470+
}
471+
472+
try {
473+
const decodedJwtPayload = jwtDecode(visitorToken.token);
474+
return NextResponse.json({
475+
visitor: {
476+
claims: {
477+
...visitorClaims.visitor.claims,
478+
...decodedJwtPayload,
479+
},
480+
},
481+
});
482+
} catch (error) {
483+
console.warn('Error decoding visitor token', error);
484+
}
485+
486+
return NextResponse.json(visitorClaims);
487+
}

packages/gitbook/src/middleware.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
getResponseCookiesForVisitorAuth,
2323
getVisitorData,
2424
normalizeVisitorURL,
25+
serveVisitorClaimsDataRequest,
2526
} from '@/lib/visitors';
2627
import { serveResizedImage } from '@/routes/image';
2728
import { cookies } from 'next/headers';
@@ -126,7 +127,6 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
126127

127128
const { url: siteRequestURL, mode } = match;
128129
const imagesContextId = getImageResizingContextId(siteRequestURL);
129-
130130
/**
131131
* Serve image resizing requests (all requests containing `/~gitbook/image`).
132132
* All URLs containing `/~gitbook/image` are rewritten to `/~gitbook/image`
@@ -154,6 +154,11 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) {
154154
});
155155
}
156156

157+
// Handler that returns visitor data for the app to consume.
158+
if (siteRequestURL.pathname.endsWith('/~gitbook/visitor')) {
159+
return serveVisitorClaimsDataRequest(request, siteRequestURL);
160+
}
161+
157162
//
158163
// Detect and extract the visitor authentication token from the request
159164
//

packages/openapi-parser/src/types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ export interface OpenAPICustomOperationProperties {
7070
'x-stability'?: OpenAPIStability;
7171
}
7272

73+
/**
74+
* Custom properties that can be defined to enable prefilling for OpenAPI blocks (e.g TryIt functionality).
75+
*/
76+
export interface OpenAPICustomPrefillProperties {
77+
'x-gitbook-prefill'?: string;
78+
}
79+
7380
export type OpenAPIStability = 'experimental' | 'alpha' | 'beta';
7481

7582
/**

0 commit comments

Comments
 (0)