diff --git a/.changeset/real-elephants-lie.md b/.changeset/real-elephants-lie.md
new file mode 100644
index 000000000..8519eb0b2
--- /dev/null
+++ b/.changeset/real-elephants-lie.md
@@ -0,0 +1,6 @@
+---
+'gitbook-v2': patch
+'gitbook': patch
+---
+
+Improving data cache management for computed content
diff --git a/.changeset/two-fans-joke.md b/.changeset/two-fans-joke.md
new file mode 100644
index 000000000..c7442bee9
--- /dev/null
+++ b/.changeset/two-fans-joke.md
@@ -0,0 +1,5 @@
+---
+'@gitbook/cache-tags': minor
+---
+
+Initial version of the package
diff --git a/bun.lock b/bun.lock
index a983f15be..ddcf20b0f 100644
--- a/bun.lock
+++ b/bun.lock
@@ -22,6 +22,17 @@
"wrangler": "^3.109.2",
},
},
+ "packages/cache-tags": {
+ "name": "@gitbook/cache-tags",
+ "version": "0.0.0",
+ "dependencies": {
+ "@gitbook/api": "0.96.1",
+ "assert-never": "^1.2.1",
+ },
+ "devDependencies": {
+ "typescript": "^5.5.3",
+ },
+ },
"packages/colors": {
"name": "@gitbook/colors",
"version": "0.2.0",
@@ -42,6 +53,7 @@
"dependencies": {
"@gitbook/api": "0.96.1",
"@gitbook/cache-do": "workspace:*",
+ "@gitbook/cache-tags": "workspace:*",
"@gitbook/colors": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
@@ -122,6 +134,7 @@
"version": "0.1.1",
"dependencies": {
"@gitbook/api": "0.96.1",
+ "@gitbook/cache-tags": "workspace:*",
"@sindresorhus/fnv1a": "^3.1.0",
"next": "^15.2.0",
"react": "^19.0.0",
@@ -618,6 +631,8 @@
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
+ "@gitbook/cache-tags": ["@gitbook/cache-tags@workspace:packages/cache-tags"],
+
"@gitbook/colors": ["@gitbook/colors@workspace:packages/colors"],
"@gitbook/emoji-codepoints": ["@gitbook/emoji-codepoints@workspace:packages/emoji-codepoints"],
diff --git a/packages/cache-tags/.gitignore b/packages/cache-tags/.gitignore
new file mode 100644
index 000000000..849ddff3b
--- /dev/null
+++ b/packages/cache-tags/.gitignore
@@ -0,0 +1 @@
+dist/
diff --git a/packages/cache-tags/README.md b/packages/cache-tags/README.md
new file mode 100644
index 000000000..14fc5c943
--- /dev/null
+++ b/packages/cache-tags/README.md
@@ -0,0 +1,3 @@
+# `@gitbook/cache-tags`
+
+Utility to generate cache tags for GitBook Open.
\ No newline at end of file
diff --git a/packages/cache-tags/package.json b/packages/cache-tags/package.json
new file mode 100644
index 000000000..aa960d125
--- /dev/null
+++ b/packages/cache-tags/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "@gitbook/cache-tags",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "development": "./src/index.ts",
+ "default": "./dist/index.js"
+ }
+ },
+ "version": "0.0.0",
+ "dependencies": {
+ "@gitbook/api": "0.96.1",
+ "assert-never": "^1.2.1"
+ },
+ "devDependencies": {
+ "typescript": "^5.5.3"
+ },
+ "scripts": {
+ "build": "tsc",
+ "typecheck": "tsc --noEmit",
+ "dev": "tsc -w"
+ },
+ "files": ["dist", "src", "README.md", "CHANGELOG.md"]
+}
diff --git a/packages/cache-tags/src/index.ts b/packages/cache-tags/src/index.ts
new file mode 100644
index 000000000..8c8701f0b
--- /dev/null
+++ b/packages/cache-tags/src/index.ts
@@ -0,0 +1,175 @@
+import type { ComputedContentSource } from '@gitbook/api';
+import assertNever from 'assert-never';
+
+/**
+ * Get a stringified cache tag for a given object.
+ */
+export function getCacheTag(
+ spec: /**
+ * All data related to a user
+ * @deprecated - in v2, no tag as this is an immutable data
+ */
+ | {
+ tag: 'user';
+ user: string;
+ }
+ /**
+ * All data related to a space
+ */
+ | {
+ tag: 'space';
+ space: string;
+ }
+ /**
+ * All data related to an integration.
+ */
+ | {
+ tag: 'integration';
+ integration: string;
+ }
+ /**
+ * All data related to a change request
+ */
+ | {
+ tag: 'change-request';
+ space: string;
+ changeRequest: string;
+ }
+ /**
+ * Immutable data related to a revision
+ * @deprecated - in v2, no tag as this is an immutable data
+ */
+ | {
+ tag: 'revision';
+ space: string;
+ revision: string;
+ }
+ /**
+ * Immutable data related to a document
+ * @deprecated - in v2, no tag as this is an immutable data
+ */
+ | {
+ tag: 'document';
+ space: string;
+ document: string;
+ }
+ /**
+ * Immutable data related to a computed document
+ * @deprecated - in v2, no tag as this is an immutable data
+ */
+ | {
+ tag: 'computed-document';
+ space: string;
+ integration: string;
+ }
+ /**
+ * All data related to the URL of a content
+ */
+ | {
+ tag: 'url';
+ hostname: string;
+ }
+ /**
+ * All data related to a site
+ */
+ | {
+ tag: 'site';
+ site: string;
+ }
+ /**
+ * All data related to an OpenAPI spec
+ */
+ | {
+ tag: 'openapi';
+ organization: string;
+ openAPISpec: string;
+ }
+): string {
+ switch (spec.tag) {
+ case 'user':
+ return `user:${spec.user}`;
+ case 'url':
+ return `url:${spec.hostname}`;
+ case 'space':
+ return `space:${spec.space}`;
+ case 'change-request':
+ return `space:${spec.space}:change-request:${spec.changeRequest}`;
+ case 'revision':
+ return `space:${spec.space}:revision:${spec.revision}`;
+ case 'document':
+ return `space:${spec.space}:document:${spec.document}`;
+ case 'computed-document':
+ return `space:${spec.space}:computed-document:${spec.integration}`;
+ case 'site':
+ return `site:${spec.site}`;
+ case 'integration':
+ return `integration:${spec.integration}`;
+ case 'openapi':
+ return `organization:${spec.organization}:openapi:${spec.openAPISpec}`;
+ default:
+ assertNever(spec);
+ }
+}
+
+/**
+ * Get the tags for a computed content source.
+ */
+export function getComputedContentSourceCacheTags(
+ inContext: {
+ spaceId: string;
+ organizationId: string;
+ },
+ source: ComputedContentSource
+) {
+ const tags: string[] = [];
+
+ // We add the dependencies as tags, to ensure that the computed content is invalidated
+ // when the dependencies are updated.
+ const dependencies = Object.values(source.dependencies ?? {});
+ if (dependencies.length > 0) {
+ dependencies.forEach((dependency) => {
+ switch (dependency.ref.kind) {
+ case 'space':
+ tags.push(
+ getCacheTag({
+ tag: 'space',
+ space: dependency.ref.space,
+ })
+ );
+ break;
+ case 'openapi':
+ tags.push(
+ getCacheTag({
+ tag: 'openapi',
+ organization: inContext.organizationId,
+ openAPISpec: dependency.ref.spec,
+ })
+ );
+ break;
+ default:
+ // Do not throw for unknown dependency types
+ // as it might mean we are lacking behind the API version
+ break;
+ }
+ });
+ } else {
+ // Push a dummy tag, as the v1 is only using the first tag
+ tags.push(
+ getCacheTag({
+ tag: 'computed-document',
+ space: inContext.spaceId,
+ integration: source.integration,
+ })
+ );
+ }
+
+ // We invalidate the computed content when a new version of the integration is deployed.
+ tags.push(
+ getCacheTag({
+ tag: 'integration',
+ integration: source.integration,
+ })
+ );
+
+ return tags;
+}
diff --git a/packages/cache-tags/tsconfig.json b/packages/cache-tags/tsconfig.json
new file mode 100644
index 000000000..92db2d902
--- /dev/null
+++ b/packages/cache-tags/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compilerOptions": {
+ "target": "esnext",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": false,
+ "declaration": true,
+ "outDir": "dist",
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "types": [
+ "bun-types" // add Bun global
+ ]
+ },
+ "include": ["src/**/*.ts", "src/**/*.tsx"],
+ "exclude": ["node_modules"]
+}
diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json
index 95097532c..1c7a6fd72 100644
--- a/packages/gitbook-v2/package.json
+++ b/packages/gitbook-v2/package.json
@@ -7,6 +7,7 @@
"react": "^19.0.0",
"react-dom": "^19.0.0",
"@gitbook/api": "0.96.1",
+ "@gitbook/cache-tags": "workspace:*",
"@sindresorhus/fnv1a": "^3.1.0",
"server-only": "^0.0.1"
},
diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[pagePath]/page.tsx b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[pagePath]/page.tsx
index 87a8cbbf8..43b9be8c4 100644
--- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[pagePath]/page.tsx
+++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[pagePath]/page.tsx
@@ -3,8 +3,8 @@ import {
generateSitePageMetadata,
generateSitePageViewport,
} from '@/components/SitePage';
+import { getCacheTag } from '@gitbook/cache-tags';
import { type RouteParams, getPagePathFromParams, getStaticSiteContext } from '@v2/app/utils';
-import { getSiteCacheTag } from '@v2/lib/cache';
import type { Metadata, Viewport } from 'next';
import { unstable_cacheTag as cacheTag } from 'next/cache';
@@ -21,7 +21,12 @@ export default async function Page(props: PageProps) {
const context = await getStaticSiteContext(params);
const pathname = getPagePathFromParams(params);
- cacheTag(getSiteCacheTag(context.site.id));
+ cacheTag(
+ getCacheTag({
+ tag: 'site',
+ site: context.site.id,
+ })
+ );
return ;
}
diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/layout.tsx b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/layout.tsx
index 9e8cb36aa..2d5de4284 100644
--- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/layout.tsx
+++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/layout.tsx
@@ -4,8 +4,8 @@ import {
generateSiteLayoutMetadata,
generateSiteLayoutViewport,
} from '@/components/SiteLayout';
+import { getCacheTag } from '@gitbook/cache-tags';
import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils';
-import { getSiteCacheTag } from '@v2/lib/cache';
import { GITBOOK_DISABLE_TRACKING } from '@v2/lib/env';
import { unstable_cacheTag as cacheTag } from 'next/cache';
@@ -21,7 +21,12 @@ export default async function SiteStaticLayout({
const context = await getStaticSiteContext(await params);
- cacheTag(getSiteCacheTag(context.site.id));
+ cacheTag(
+ getCacheTag({
+ tag: 'site',
+ site: context.site.id,
+ })
+ );
return (
diff --git a/packages/gitbook-v2/src/lib/cache.ts b/packages/gitbook-v2/src/lib/cache.ts
deleted file mode 100644
index 38e1d334c..000000000
--- a/packages/gitbook-v2/src/lib/cache.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- * Get the cache tag for a site.
- */
-export function getSiteCacheTag(siteId: string) {
- return `site:${siteId}`;
-}
-
-/**
- * Get the cache tag for a hostname.
- */
-export function getHostnameCacheTag(hostname: string) {
- return `url:${hostname}`;
-}
-
-/**
- * Get the cache tag for an OpenAPI spec.
- */
-export function getOpenAPISpecCacheTag(organizationId: string, slug: string) {
- return `organization:${organizationId}:openapi:${slug}`;
-}
-
-/**
- * Get the cache tag for a space.
- */
-export function getSpaceCacheTag(spaceId: string) {
- return `space:${spaceId}`;
-}
-
-/**
- * Get the cache tag for a change request.
- */
-export function getChangeRequestCacheTag(spaceId: string, changeRequestId: string) {
- return `space:${spaceId}:change-request:${changeRequestId}`;
-}
diff --git a/packages/gitbook-v2/src/lib/data/api.ts b/packages/gitbook-v2/src/lib/data/api.ts
index 72698b430..a3a254a28 100644
--- a/packages/gitbook-v2/src/lib/data/api.ts
+++ b/packages/gitbook-v2/src/lib/data/api.ts
@@ -1,13 +1,7 @@
import { type ComputedContentSource, GitBookAPI } from '@gitbook/api';
+import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags';
import { GITBOOK_API_TOKEN, GITBOOK_API_URL, GITBOOK_USER_AGENT } from '@v2/lib/env';
import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache';
-import {
- getChangeRequestCacheTag,
- getHostnameCacheTag,
- getOpenAPISpecCacheTag,
- getSiteCacheTag,
- getSpaceCacheTag,
-} from '../cache';
import type { GitBookDataFetcher } from './types';
interface DataFetcherInput {
@@ -125,6 +119,7 @@ export function createDataFetcher(input: DataFetcherInput = commonInput): GitBoo
},
getComputedDocument(params) {
return getComputedDocument(input, {
+ organizationId: params.organizationId,
spaceId: params.spaceId,
source: params.source,
});
@@ -183,7 +178,12 @@ async function getSpace(
'use cache';
cacheLife('days');
- cacheTag(getSpaceCacheTag(params.spaceId));
+ cacheTag(
+ getCacheTag({
+ tag: 'space',
+ space: params.spaceId,
+ })
+ );
const res = await getAPI(input).spaces.getSpaceById(params.spaceId, {
shareKey: params.shareKey,
@@ -207,7 +207,13 @@ async function getChangeRequest(
params.spaceId,
params.changeRequestId
);
- cacheTag(getChangeRequestCacheTag(params.spaceId, res.data.id));
+ cacheTag(
+ getCacheTag({
+ tag: 'change-request',
+ space: params.spaceId,
+ changeRequest: res.data.id,
+ })
+ );
return res.data;
} catch (error) {
if (checkHasErrorCode(error, 404)) {
@@ -335,14 +341,24 @@ async function getComputedDocument(
input: DataFetcherInput,
params: {
spaceId: string;
+ organizationId: string;
source: ComputedContentSource;
}
) {
'use cache';
- // TODO: we need to resolve dependencies and pass them in the cache key
cacheLife('days');
+ cacheTag(
+ ...getComputedContentSourceCacheTags(
+ {
+ spaceId: params.spaceId,
+ organizationId: params.organizationId,
+ },
+ params.source
+ )
+ );
+
const res = await getAPI(input).spaces.getComputedDocument(params.spaceId, {
source: params.source,
});
@@ -386,7 +402,13 @@ async function getLatestOpenAPISpecVersionContent(
) {
'use cache';
- cacheTag(getOpenAPISpecCacheTag(params.organizationId, params.slug));
+ cacheTag(
+ getCacheTag({
+ tag: 'openapi',
+ organization: params.organizationId,
+ openAPISpec: params.slug,
+ })
+ );
cacheLife('days');
try {
@@ -417,7 +439,12 @@ async function getPublishedContentByUrl(
const { url, visitorAuthToken, redirectOnError } = params;
const hostname = new URL(url).hostname;
- cacheTag(getHostnameCacheTag(hostname));
+ cacheTag(
+ getCacheTag({
+ tag: 'url',
+ hostname,
+ })
+ );
cacheLife('days');
const res = await getAPI(input).urls.getPublishedContentByUrl({
@@ -427,7 +454,12 @@ async function getPublishedContentByUrl(
});
if ('site' in res.data) {
- cacheTag(getSiteCacheTag(res.data.site));
+ cacheTag(
+ getCacheTag({
+ tag: 'site',
+ site: res.data.site,
+ })
+ );
}
return res.data;
@@ -443,7 +475,12 @@ async function getPublishedContentSite(
) {
'use cache';
- cacheTag(getSiteCacheTag(params.siteId));
+ cacheTag(
+ getCacheTag({
+ tag: 'site',
+ site: params.siteId,
+ })
+ );
cacheLife('days');
const res = await getAPI(input).orgs.getPublishedContentSite(
@@ -467,7 +504,12 @@ async function getSiteRedirectBySource(
) {
'use cache';
- cacheTag(getSiteCacheTag(params.siteId));
+ cacheTag(
+ getCacheTag({
+ tag: 'site',
+ site: params.siteId,
+ })
+ );
cacheLife('days');
try {
diff --git a/packages/gitbook-v2/src/lib/data/types.ts b/packages/gitbook-v2/src/lib/data/types.ts
index 0a0bbe2a0..b86fb09fc 100644
--- a/packages/gitbook-v2/src/lib/data/types.ts
+++ b/packages/gitbook-v2/src/lib/data/types.ts
@@ -103,6 +103,7 @@ export interface GitBookDataFetcher {
* Get a computed document by its space ID and computed source.
*/
getComputedDocument(params: {
+ organizationId: string;
spaceId: string;
source: api.ComputedContentSource;
}): Promise;
diff --git a/packages/gitbook-v2/src/lib/data/utils.ts b/packages/gitbook-v2/src/lib/data/utils.ts
index b65a89ae6..4e7f4a436 100644
--- a/packages/gitbook-v2/src/lib/data/utils.ts
+++ b/packages/gitbook-v2/src/lib/data/utils.ts
@@ -1,4 +1,4 @@
-import type { RevisionPageDocument } from '@gitbook/api';
+import type { RevisionPageDocument, Space } from '@gitbook/api';
import type { GitBookDataFetcher } from './types';
/**
@@ -6,14 +6,18 @@ import type { GitBookDataFetcher } from './types';
*/
export async function getPageDocument(
dataFetcher: GitBookDataFetcher,
- spaceId: string,
+ space: Space,
page: RevisionPageDocument
) {
if (page.documentId) {
- return dataFetcher.getDocument({ spaceId, documentId: page.documentId });
+ return dataFetcher.getDocument({ spaceId: space.id, documentId: page.documentId });
}
if (page.computed) {
- return dataFetcher.getComputedDocument({ spaceId, source: page.computed });
+ return dataFetcher.getComputedDocument({
+ organizationId: space.organization,
+ spaceId: space.id,
+ source: page.computed,
+ });
}
return null;
diff --git a/packages/gitbook/package.json b/packages/gitbook/package.json
index bbbcf467b..8c696cb8b 100644
--- a/packages/gitbook/package.json
+++ b/packages/gitbook/package.json
@@ -18,6 +18,7 @@
"dependencies": {
"@gitbook/api": "0.96.1",
"@gitbook/cache-do": "workspace:*",
+ "@gitbook/cache-tags": "workspace:*",
"@gitbook/colors": "workspace:*",
"@gitbook/emoji-codepoints": "workspace:*",
"@gitbook/icons": "workspace:*",
diff --git a/packages/gitbook/src/app/middleware/(space)/~gitbook/pdf/page.tsx b/packages/gitbook/src/app/middleware/(space)/~gitbook/pdf/page.tsx
index e698a7c9f..691ec72c9 100644
--- a/packages/gitbook/src/app/middleware/(space)/~gitbook/pdf/page.tsx
+++ b/packages/gitbook/src/app/middleware/(space)/~gitbook/pdf/page.tsx
@@ -231,7 +231,7 @@ async function PDFPageDocument(props: {
}) {
const { page, context } = props;
const { space } = context;
- const document = await getPageDocument(context.dataFetcher, space.id, page);
+ const document = await getPageDocument(context.dataFetcher, space, page);
return (
diff --git a/packages/gitbook/src/components/SitePage/SitePage.tsx b/packages/gitbook/src/components/SitePage/SitePage.tsx
index c8af02d28..50ee21f33 100644
--- a/packages/gitbook/src/components/SitePage/SitePage.tsx
+++ b/packages/gitbook/src/components/SitePage/SitePage.tsx
@@ -67,7 +67,7 @@ export async function SitePage(props: SitePageProps) {
const withSections = Boolean(sections && sections.list.length > 0);
const headerOffset = { sectionsHeader: withSections, topHeader: withTopHeader };
- const document = await getPageDocument(context.dataFetcher, context.space.id, page);
+ const document = await getPageDocument(context.dataFetcher, context.space, page);
return (
<>
diff --git a/packages/gitbook/src/lib/api.ts b/packages/gitbook/src/lib/api.ts
index ecd403946..6ce3d41bf 100644
--- a/packages/gitbook/src/lib/api.ts
+++ b/packages/gitbook/src/lib/api.ts
@@ -1,4 +1,5 @@
import { AsyncLocalStorage } from 'node:async_hooks';
+import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags';
import 'server-only';
import {
@@ -14,7 +15,6 @@ import {
type RevisionReusableContent,
} from '@gitbook/api';
import type { GitBookDataFetcher } from '@v2/lib/data/types';
-import assertNever from 'assert-never';
import { headers } from 'next/headers';
import { batch } from './async';
@@ -171,7 +171,7 @@ export type PublishedContentWithCache =
export const getUserById = cache({
name: 'api.getUserById',
tag: (userId) =>
- getAPICacheTag({
+ getCacheTag({
tag: 'user',
user: userId,
}),
@@ -204,7 +204,7 @@ export const getUserById = cache({
export const getLatestOpenAPISpecVersionContent = cache({
name: 'api.getLatestOpenApiSpecVersionContent',
tag: (organization, openAPISpec) =>
- getAPICacheTag({
+ getCacheTag({
tag: 'openapi',
organization,
openAPISpec,
@@ -240,7 +240,7 @@ export const getLatestOpenAPISpecVersionContent = cache({
export const getPublishedContentByUrl = cache({
name: 'api.getPublishedContentByUrl.v4',
tag: (url) =>
- getAPICacheTag({
+ getCacheTag({
tag: 'url',
hostname: new URL(url).hostname,
}),
@@ -301,7 +301,7 @@ export const getPublishedContentByUrl = cache({
*/
export const getSpace = cache({
name: 'api.getSpace',
- tag: (spaceId) => getAPICacheTag({ tag: 'space', space: spaceId }),
+ tag: (spaceId) => getCacheTag({ tag: 'space', space: spaceId }),
get: async (spaceId: string, shareKey: string | undefined, options: CacheFunctionOptions) => {
const apiCtx = await api();
const response = await apiCtx.client.spaces.getSpaceById(
@@ -330,7 +330,7 @@ function checkHasErrorCode(error: unknown, code: number) {
export const getChangeRequest = cache({
name: 'api.getChangeRequest',
tag: (spaceId, changeRequestId) =>
- getAPICacheTag({ tag: 'change-request', space: spaceId, changeRequest: changeRequestId }),
+ getCacheTag({ tag: 'change-request', space: spaceId, changeRequest: changeRequestId }),
get: async (spaceId: string, changeRequestId: string, options: CacheFunctionOptions) => {
const apiCtx = await api();
try {
@@ -379,7 +379,7 @@ const getAPIContextId = async () => {
export const getRevision = cache({
name: 'api.getRevision.v2',
tag: (spaceId, revisionId) =>
- getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
+ getCacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
getKeySuffix: getAPIContextId,
get: async (
spaceId: string,
@@ -411,7 +411,7 @@ export const getRevision = cache({
export const getRevisionPages = cache({
name: 'api.getRevisionPages.v4',
tag: (spaceId, revisionId) =>
- getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
+ getCacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
getKeySuffix: getAPIContextId,
get: async (
spaceId: string,
@@ -446,7 +446,7 @@ export const getRevisionPages = cache({
export const getRevisionPageByPath = cache({
name: 'api.getRevisionPageByPath.v3',
tag: (spaceId, revisionId) =>
- getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
+ getCacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
getKeySuffix: getAPIContextId,
get: async (
spaceId: string,
@@ -492,7 +492,7 @@ export const getRevisionPageByPath = cache({
const getRevisionFileById = cache({
name: 'api.getRevisionFile.v3',
tag: (spaceId, revisionId) =>
- getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
+ getCacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
get: async (
spaceId: string,
revisionId: string,
@@ -528,7 +528,7 @@ const getRevisionFileById = cache({
const getRevisionReusableContentById = cache({
name: 'api.getRevisionReusableContentById.v1',
tag: (spaceId, revisionId) =>
- getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
+ getCacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
getKeySuffix: getAPIContextId,
get: async (
spaceId: string,
@@ -569,7 +569,7 @@ const getRevisionReusableContentById = cache({
const getRevisionAllFiles = cache({
name: 'api.getRevisionAllFiles.v2',
tag: (spaceId, revisionId) =>
- getAPICacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
+ getCacheTag({ tag: 'revision', space: spaceId, revision: revisionId }),
get: async (spaceId: string, revisionId: string, options: CacheFunctionOptions) => {
const response = await getAll(
async (params) => {
@@ -684,7 +684,7 @@ export const getReusableContent = async (
export const getDocument = cache({
name: 'api.getDocument.v2',
tag: (spaceId, documentId) =>
- getAPICacheTag({ tag: 'document', space: spaceId, document: documentId }),
+ getCacheTag({ tag: 'document', space: spaceId, document: documentId }),
getKeySuffix: getAPIContextId,
get: async (spaceId: string, documentId: string, options: CacheFunctionOptions) => {
const apiCtx = await api();
@@ -712,14 +712,21 @@ export const getDocument = cache({
*/
export const getComputedDocument = cache({
name: 'api.getComputedDocument',
- tag: (spaceId, source) =>
- getAPICacheTag({
- tag: 'computed-document',
- space: spaceId,
- integration: source.integration,
- }),
+ tag: (organizationId, spaceId, source) =>
+ getComputedContentSourceCacheTags(
+ {
+ organizationId,
+ spaceId,
+ },
+ source
+ )[0],
getKeySuffix: getAPIContextId,
- get: async (spaceId: string, source: ComputedContentSource, options: CacheFunctionOptions) => {
+ get: async (
+ _organizationId: string,
+ spaceId: string,
+ source: ComputedContentSource,
+ options: CacheFunctionOptions
+ ) => {
const apiCtx = await api();
const response = await apiCtx.client.spaces.getComputedDocument(
spaceId,
@@ -750,7 +757,7 @@ function validateSiteRedirectSource(source: string) {
*/
export const getSiteRedirectBySource = cache({
name: 'api.getSiteRedirectBySource',
- tag: ({ siteId }) => getAPICacheTag({ tag: 'site', site: siteId }),
+ tag: ({ siteId }) => getCacheTag({ tag: 'site', site: siteId }),
getKeySuffix: getAPIContextId,
get: async (
args: {
@@ -811,7 +818,7 @@ export const getSiteRedirectBySource = cache({
*/
export const getSite = cache({
name: 'api.getSite',
- tag: (_organizationId, siteId) => getAPICacheTag({ tag: 'site', site: siteId }),
+ tag: (_organizationId, siteId) => getCacheTag({ tag: 'site', site: siteId }),
getKeySuffix: getAPIContextId,
get: async (organizationId: string, siteId: string, options: CacheFunctionOptions) => {
const apiCtx = await api();
@@ -830,7 +837,7 @@ export const getSite = cache({
*/
export const getPublishedContentSite = cache({
name: 'api.getPublishedContentSite',
- tag: ({ siteId }) => getAPICacheTag({ tag: 'site', site: siteId }),
+ tag: ({ siteId }) => getCacheTag({ tag: 'site', site: siteId }),
getKeySuffix: getAPIContextId,
get: async (
args: {
@@ -907,7 +914,7 @@ export async function getSpaceContentData(
*/
export const searchSiteContent = cache({
name: 'api.searchSiteContent',
- tag: (_organizationId, siteId) => getAPICacheTag({ tag: 'site', site: siteId }),
+ tag: (_organizationId, siteId) => getCacheTag({ tag: 'site', site: siteId }),
getKeySuffix: getAPIContextId,
get: async (
organizationId: string,
@@ -947,7 +954,7 @@ export const searchSiteContent = cache({
*/
export const renderIntegrationUi = cache({
name: 'api.renderIntegrationUi',
- tag: (integrationName) => getAPICacheTag({ tag: 'integration', integration: integrationName }),
+ tag: (integrationName) => getCacheTag({ tag: 'integration', integration: integrationName }),
get: async (
integrationName: string,
request: RequestRenderIntegrationUI,
@@ -971,7 +978,7 @@ export const renderIntegrationUi = cache({
*/
export const getEmbedByUrlInSpace = cache({
name: 'api.getEmbedByUrlInSpace',
- tag: (spaceId) => getAPICacheTag({ tag: 'space', space: spaceId }),
+ tag: (spaceId) => getCacheTag({ tag: 'space', space: spaceId }),
get: async (spaceId: string, url: string, options: CacheFunctionOptions) => {
const apiCtx = await api();
const response = await apiCtx.client.spaces.getEmbedByUrlInSpace(
@@ -986,99 +993,6 @@ export const getEmbedByUrlInSpace = cache({
},
});
-/**
- * Create a cache tag for the API.
- */
-export function getAPICacheTag(
- spec: // All data related to a user
- | {
- tag: 'user';
- user: string;
- }
- // All data related to a space
- | {
- tag: 'space';
- space: string;
- }
- // All data related to an integration
- | {
- tag: 'integration';
- integration: string;
- }
- // All data related to a change request
- | {
- tag: 'change-request';
- space: string;
- changeRequest: string;
- }
- // Immutable data related to a revision
- | {
- tag: 'revision';
- space: string;
- revision: string;
- }
- // Immutable data related to a document
- | {
- tag: 'document';
- space: string;
- document: string;
- }
- // Immutable data related to a computed document
- | {
- tag: 'computed-document';
- space: string;
- integration: string;
- }
- // All data related to the URL of a content
- | {
- tag: 'url';
- hostname: string;
- }
- // All data related to a collection
- | {
- tag: 'collection';
- collection: string;
- }
- // All data related to a site
- | {
- tag: 'site';
- site: string;
- }
- // All data related to an OpenAPI spec
- | {
- tag: 'openapi';
- organization: string;
- openAPISpec: string;
- }
-): string {
- switch (spec.tag) {
- case 'user':
- return `user:${spec.user}`;
- case 'url':
- return `url:${spec.hostname}`;
- case 'space':
- return `space:${spec.space}`;
- case 'change-request':
- return `space:${spec.space}:change-request:${spec.changeRequest}`;
- case 'revision':
- return `space:${spec.space}:revision:${spec.revision}`;
- case 'document':
- return `space:${spec.space}:document:${spec.document}`;
- case 'computed-document':
- return `space:${spec.space}:computed-document:${spec.integration}`;
- case 'collection':
- return `collection:${spec.collection}`;
- case 'site':
- return `site:${spec.site}`;
- case 'integration':
- return `integration:${spec.integration}`;
- case 'openapi':
- return `organization:${spec.organization}:openapi:${spec.openAPISpec}`;
- default:
- assertNever(spec);
- }
-}
-
/**
* Return the user agent to use for API requests.
*/
diff --git a/packages/gitbook/src/lib/references.tsx b/packages/gitbook/src/lib/references.tsx
index eed8261c3..e2f6416ef 100644
--- a/packages/gitbook/src/lib/references.tsx
+++ b/packages/gitbook/src/lib/references.tsx
@@ -130,7 +130,7 @@ export async function resolveContentRef(
text = `#${anchor}`;
if (resolveAnchorText) {
- const document = await getPageDocument(dataFetcher, space.id, page);
+ const document = await getPageDocument(dataFetcher, space, page);
if (document) {
const block = getBlockById(document, anchor);
if (block) {
diff --git a/packages/gitbook/src/lib/v1.ts b/packages/gitbook/src/lib/v1.ts
index cc0900613..d4314c896 100644
--- a/packages/gitbook/src/lib/v1.ts
+++ b/packages/gitbook/src/lib/v1.ts
@@ -128,7 +128,7 @@ async function getDataFetcherV1(): Promise {
},
getComputedDocument(params) {
- return getComputedDocument(params.spaceId, params.source);
+ return getComputedDocument(params.organizationId, params.spaceId, params.source);
},
getRevisionPages(params) {