diff --git a/bun.lockb b/bun.lockb index de48a1be6..da66de451 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/packages/gitbook/package.json b/packages/gitbook/package.json index c2f62876c..9d724d952 100644 --- a/packages/gitbook/package.json +++ b/packages/gitbook/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "@geist-ui/icons": "^1.0.2", - "@gitbook/api": "^0.56.0", + "@gitbook/api": "^0.58.0", "@gitbook/react-math": "workspace:*", "@gitbook/react-openapi": "workspace:*", "@gitbook/react-contentkit": "workspace:*", diff --git a/packages/gitbook/src/app/(space)/(content)/[[...pathname]]/page.tsx b/packages/gitbook/src/app/(space)/(content)/[[...pathname]]/page.tsx index d77f39eec..947d84693 100644 --- a/packages/gitbook/src/app/(space)/(content)/[[...pathname]]/page.tsx +++ b/packages/gitbook/src/app/(space)/(content)/[[...pathname]]/page.tsx @@ -64,6 +64,7 @@ export default async function Page(props: { const withPageFeedback = customization.feedback.enabled; const contentRefContext: ContentRefContext = { + siteContext: 'siteId' in contentPointer ? contentPointer : null, space, revisionId: contentTarget.revisionId, pages, diff --git a/packages/gitbook/src/app/(space)/(core)/robots.txt/route.ts b/packages/gitbook/src/app/(space)/(core)/robots.txt/route.ts index 7f199eed5..4b4e9f6df 100644 --- a/packages/gitbook/src/app/(space)/(core)/robots.txt/route.ts +++ b/packages/gitbook/src/app/(space)/(core)/robots.txt/route.ts @@ -14,7 +14,10 @@ export const runtime = 'edge'; */ export async function GET(req: NextRequest) { const pointer = getContentPointer(); - const space = await getSpace(pointer.spaceId); + const space = await getSpace( + pointer.spaceId, + 'siteId' in pointer ? pointer.siteShareKey : undefined, + ); const parent = 'siteId' in pointer ? await getSite(pointer.organizationId, pointer.siteId) diff --git a/packages/gitbook/src/app/(space)/(core)/sitemap.xml/route.ts b/packages/gitbook/src/app/(space)/(core)/sitemap.xml/route.ts index 4a6375945..398e2af69 100644 --- a/packages/gitbook/src/app/(space)/(core)/sitemap.xml/route.ts +++ b/packages/gitbook/src/app/(space)/(core)/sitemap.xml/route.ts @@ -14,7 +14,11 @@ export const runtime = 'edge'; * Generate a sitemap.xml for the current space. */ export async function GET(req: NextRequest) { - const { pages: rootPages } = await getSpaceContentData(getContentPointer()); + const pointer = getContentPointer(); + const { pages: rootPages } = await getSpaceContentData( + pointer, + 'siteId' in pointer ? pointer.siteShareKey : undefined, + ); const pages = flattenPages(rootPages, (page) => !page.hidden); const urls = pages.map(({ page, depth }) => { // Decay priority with depth diff --git a/packages/gitbook/src/app/(space)/(core)/~gitbook/icon/route.tsx b/packages/gitbook/src/app/(space)/(core)/~gitbook/icon/route.tsx index a1d00015a..d6bc98a51 100644 --- a/packages/gitbook/src/app/(space)/(core)/~gitbook/icon/route.tsx +++ b/packages/gitbook/src/app/(space)/(core)/~gitbook/icon/route.tsx @@ -46,7 +46,7 @@ export async function GET(req: NextRequest) { const spaceId = pointer.spaceId; const [space, customization] = await Promise.all([ - getSpace(spaceId), + getSpace(spaceId, 'siteId' in pointer ? pointer.siteShareKey : undefined), 'siteId' in pointer ? getCurrentSiteCustomization(pointer) : getSpaceCustomization(spaceId), ]); const parent = diff --git a/packages/gitbook/src/app/(space)/(core)/~gitbook/pdf/page.tsx b/packages/gitbook/src/app/(space)/(core)/~gitbook/pdf/page.tsx index 0fa16cfbf..fdc4aa148 100644 --- a/packages/gitbook/src/app/(space)/(core)/~gitbook/pdf/page.tsx +++ b/packages/gitbook/src/app/(space)/(core)/~gitbook/pdf/page.tsx @@ -42,7 +42,10 @@ export const runtime = 'edge'; export async function generateMetadata(): Promise { const contentPointer = getContentPointer(); const [space, customization] = await Promise.all([ - getSpace(contentPointer.spaceId), + getSpace( + contentPointer.spaceId, + 'siteId' in contentPointer ? contentPointer.siteShareKey : undefined, + ), 'siteId' in contentPointer ? getCurrentSiteCustomization(contentPointer) : getSpaceCustomization(contentPointer.spaceId), @@ -72,7 +75,10 @@ export default async function PDFHTMLOutput(props: { searchParams: { [key: strin 'siteId' in contentPointer ? getCurrentSiteCustomization(contentPointer) : getSpaceCustomization(contentPointer.spaceId), - getSpaceContentData(contentPointer), + getSpaceContentData( + contentPointer, + 'siteId' in contentPointer ? contentPointer.siteShareKey : undefined, + ), ]); const language = getSpaceLanguage(customization); @@ -170,6 +176,7 @@ export default async function PDFHTMLOutput(props: { searchParams: { [key: strin space={space} page={page} refContext={{ + siteContext: 'siteId' in contentPointer ? contentPointer : null, space, revisionId: contentTarget.revisionId, pages: rootPages, diff --git a/packages/gitbook/src/app/(space)/fetch.ts b/packages/gitbook/src/app/(space)/fetch.ts index a3a17ae1f..675568382 100644 --- a/packages/gitbook/src/app/(space)/fetch.ts +++ b/packages/gitbook/src/app/(space)/fetch.ts @@ -71,6 +71,8 @@ export function getContentPointer(): ContentPointer | SiteContentPointer { export async function fetchSpaceData() { const content = getContentPointer(); + const siteShareKey = 'siteId' in content ? content.siteShareKey : undefined; + const [{ space, contentTarget, pages, customization, scripts }, parentSite] = await Promise.all( 'siteId' in content ? [ @@ -78,10 +80,10 @@ export async function fetchSpaceData() { fetchParentSite({ organizationId: content.organizationId, siteId: content.siteId, - siteShareKey: content.siteShareKey, + siteShareKey, }), ] - : [getSpaceData(content)], + : [getSpaceData(content, siteShareKey)], ); const parent = await (parentSite ?? fetchParentCollection(space)); @@ -104,9 +106,10 @@ export async function fetchSpaceData() { */ export async function fetchPageData(params: PagePathParams | PageIdParams) { const content = getContentPointer(); + const siteShareKey = 'siteId' in content ? content.siteShareKey : undefined; const { space, contentTarget, pages, customization, scripts } = await ('siteId' in content ? getCurrentSiteData(content) - : getSpaceData(content)); + : getSpaceData(content, siteShareKey)); const page = await resolvePage(contentTarget, pages, params); const [parent, document] = await Promise.all([ diff --git a/packages/gitbook/src/components/Search/server-actions.tsx b/packages/gitbook/src/components/Search/server-actions.tsx index d382b3a66..c692cf92f 100644 --- a/packages/gitbook/src/components/Search/server-actions.tsx +++ b/packages/gitbook/src/components/Search/server-actions.tsx @@ -165,7 +165,7 @@ export const streamAskQuestion = streamResponse(async function* (spaceId: string const stream = api .api() .spaces.streamAskInSpace(spaceId, { query, format: 'document', details: true }); - const pagesPromise = api.getSpaceContentData({ spaceId }); + const pagesPromise = api.getSpaceContentData({ spaceId }, undefined); for await (const chunk of stream) { // We run the AI search and fetch the pages in parallel diff --git a/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx b/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx index 9e044f83b..8ee3475c3 100644 --- a/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx +++ b/packages/gitbook/src/components/SpaceLayout/SpaceLayout.tsx @@ -17,7 +17,7 @@ import { CONTAINER_STYLE } from '@/components/layout'; import { ColorDebugger } from '@/components/primitives/ColorDebugger'; import { SearchModal } from '@/components/Search'; import { TableOfContents } from '@/components/TableOfContents'; -import { ContentPointer, ContentTarget } from '@/lib/api'; +import { ContentPointer, ContentTarget, SiteContentPointer } from '@/lib/api'; import { ContentRefContext } from '@/lib/references'; import { tcls } from '@/lib/tailwind'; @@ -25,7 +25,7 @@ import { tcls } from '@/lib/tailwind'; * Render the entire content of the space (header, table of contents, footer, and page content). */ export function SpaceLayout(props: { - content: ContentPointer; + content: ContentPointer | SiteContentPointer; contentTarget: ContentTarget; space: Space; parent: Site | Collection | null; @@ -50,6 +50,7 @@ export function SpaceLayout(props: { const withTopHeader = customization.header.preset !== CustomizationHeaderPreset.None; const contentRefContext: ContentRefContext = { + siteContext: 'siteId' in content ? content : null, space, revisionId: contentTarget.revisionId, pages, diff --git a/packages/gitbook/src/lib/api.ts b/packages/gitbook/src/lib/api.ts index da5759d2b..d77cf5be2 100644 --- a/packages/gitbook/src/lib/api.ts +++ b/packages/gitbook/src/lib/api.ts @@ -282,11 +282,17 @@ export const getPublishedContentByUrl = cache( */ export const getSpace = cache( 'api.getSpace', - async (spaceId: string, options: CacheFunctionOptions) => { - const response = await api().spaces.getSpaceById(spaceId, { - ...noCacheFetchOptions, - signal: options.signal, - }); + async (spaceId: string, shareKey: string | undefined, options: CacheFunctionOptions) => { + const response = await api().spaces.getSpaceById( + spaceId, + { + shareKey, + }, + { + ...noCacheFetchOptions, + signal: options.signal, + }, + ); return cacheResponse(response, { revalidateBefore: 60 * 60, tags: [getAPICacheTag({ tag: 'space', space: spaceId })], @@ -795,7 +801,7 @@ export const getSiteIntegrationScripts = cache( */ export async function getCurrentSiteData(pointer: SiteContentPointer) { const [{ space, pages, contentTarget }, { customization, scripts }] = await Promise.all([ - getSpaceData(pointer), + getSpaceData(pointer, pointer.siteShareKey), getCurrentSiteLayoutData(pointer), ]); @@ -940,9 +946,9 @@ export const getCollectionSpaces = cache( /** * Fetch all the data to render a space at once. */ -export async function getSpaceData(pointer: ContentPointer) { +export async function getSpaceData(pointer: ContentPointer, shareKey: string | undefined) { const [{ space, pages, contentTarget }, { customization, scripts }] = await Promise.all([ - getSpaceContentData(pointer), + getSpaceContentData(pointer, shareKey), getSpaceLayoutData(pointer.spaceId), ]); @@ -960,9 +966,9 @@ export async function getSpaceData(pointer: ContentPointer) { * This function executes the requests in parallel and should be used as early as possible * instead of calling the individual functions. */ -export async function getSpaceContentData(pointer: ContentPointer) { +export async function getSpaceContentData(pointer: ContentPointer, shareKey: string | undefined) { const [space, changeRequest] = await Promise.all([ - getSpace(pointer.spaceId), + getSpace(pointer.spaceId, shareKey), pointer.changeRequestId ? getChangeRequest(pointer.spaceId, pointer.changeRequestId) : null, ]); diff --git a/packages/gitbook/src/lib/references.ts b/packages/gitbook/src/lib/references.ts index 2a6699a61..c5b7f48ed 100644 --- a/packages/gitbook/src/lib/references.ts +++ b/packages/gitbook/src/lib/references.ts @@ -3,6 +3,7 @@ import assertNever from 'assert-never'; import { ContentPointer, + SiteContentPointer, getCollection, getDocument, getRevisionFile, @@ -36,6 +37,11 @@ export interface ContentRefContext extends PageHrefContext { */ baseUrl?: string; + /** + * Site in which we are resolving the content reference. + * If null, the site is not known (legacy published content mode) + */ + siteContext: SiteContentPointer | null; /** * Space in which we are resolving the content reference. */ @@ -74,7 +80,7 @@ export async function resolveContentRef( options: ResolveContentRefOptions = {}, ): Promise { const { resolveAnchorText = false } = options; - const { space, revisionId, pages, page: activePage, ...linksContext } = context; + const { siteContext, space, revisionId, pages, page: activePage, ...linksContext } = context; switch (contentRef.kind) { case 'url': { @@ -102,7 +108,7 @@ export async function resolveContentRef( case 'anchor': case 'page': { if (contentRef.space && contentRef.space !== space.id) { - return resolveContentRefInSpace(contentRef.space, contentRef); + return resolveContentRefInSpace(contentRef.space, siteContext, contentRef); } const resolvePageResult = @@ -175,7 +181,12 @@ export async function resolveContentRef( const targetSpace = contentRef.space === space.id ? space - : await ignoreAPIError(getSpace(contentRef.space)); + : await ignoreAPIError( + getSpace( + contentRef.space, + siteContext?.siteShareKey ? siteContext.siteShareKey : undefined, + ), + ); if (!targetSpace) { return { @@ -239,12 +250,16 @@ export async function resolveContentRef( } } -async function resolveContentRefInSpace(spaceId: string, contentRef: ContentRef) { +async function resolveContentRefInSpace( + spaceId: string, + siteContext: SiteContentPointer | null, + contentRef: ContentRef, +) { const pointer: ContentPointer = { spaceId, }; - const result = await ignoreAPIError(getSpaceContentData(pointer)); + const result = await ignoreAPIError(getSpaceContentData(pointer, siteContext?.siteShareKey)); if (!result) { return null; } @@ -258,6 +273,7 @@ async function resolveContentRefInSpace(spaceId: string, contentRef: ContentRef) } const resolved = await resolveContentRef(contentRef, { + siteContext, space, revisionId: space.revision, pages, diff --git a/packages/gitbook/src/middleware.ts b/packages/gitbook/src/middleware.ts index 85360de78..20cb478e8 100644 --- a/packages/gitbook/src/middleware.ts +++ b/packages/gitbook/src/middleware.ts @@ -172,11 +172,14 @@ export async function middleware(request: NextRequest) { // Start fetching everything as soon as possible, but do not block the middleware on it // the cache will handle concurrent calls await waitUntil( - getSpaceContentData({ - spaceId: resolved.space, - changeRequestId: resolved.changeRequest, - revisionId: resolved.revision, - }), + getSpaceContentData( + { + spaceId: resolved.space, + changeRequestId: resolved.changeRequest, + revisionId: resolved.revision, + }, + 'site' in resolved ? resolved.shareKey : undefined, + ), ); const { scripts } = await ('site' in resolved @@ -462,7 +465,7 @@ async function lookupSpaceInMultiIdMode(request: NextRequest, url: URL): Promise authToken: apiToken, userAgent: userAgent(), }), - () => getSpace.revalidate(spaceId), + () => getSpace.revalidate(spaceId, undefined), ); const cookies: LookupCookies = { diff --git a/packages/react-contentkit/package.json b/packages/react-contentkit/package.json index cc426be55..69ea324d2 100644 --- a/packages/react-contentkit/package.json +++ b/packages/react-contentkit/package.json @@ -10,7 +10,7 @@ }, "dependencies": { "classnames": "^2.5.1", - "@gitbook/api": "^0.51.0", + "@gitbook/api": "^0.58.0", "assert-never": "^1.2.1" }, "peerDependencies": {