mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-20 17:43:24 +00:00
Use API cache endpoint and service binding in Cloudflare (#2952)
This commit is contained in:
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@gitbook/cache-tags': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
Export function `getCacheTagForURL` to easily get the cache tag for a URL.
|
||||||
@@ -44,6 +44,7 @@ runs:
|
|||||||
GITBOOK_SECRET: ${{ inputs.opItem }}/GITBOOK_SECRET
|
GITBOOK_SECRET: ${{ inputs.opItem }}/GITBOOK_SECRET
|
||||||
GITBOOK_APP_URL: ${{ inputs.opItem }}/GITBOOK_APP_URL
|
GITBOOK_APP_URL: ${{ inputs.opItem }}/GITBOOK_APP_URL
|
||||||
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
|
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
|
||||||
|
GITBOOK_API_PUBLIC_URL: ${{ inputs.opItem }}/GITBOOK_API_PUBLIC_URL
|
||||||
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
|
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
|
||||||
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
|
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
|
||||||
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
|
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ runs:
|
|||||||
GITBOOK_SECRET: ${{ inputs.opItem }}/GITBOOK_SECRET
|
GITBOOK_SECRET: ${{ inputs.opItem }}/GITBOOK_SECRET
|
||||||
GITBOOK_APP_URL: ${{ inputs.opItem }}/GITBOOK_APP_URL
|
GITBOOK_APP_URL: ${{ inputs.opItem }}/GITBOOK_APP_URL
|
||||||
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
|
GITBOOK_API_URL: ${{ inputs.opItem }}/GITBOOK_API_URL
|
||||||
|
GITBOOK_API_PUBLIC_URL: ${{ inputs.opItem }}/GITBOOK_API_PUBLIC_URL
|
||||||
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
|
GITBOOK_API_TOKEN: ${{ inputs.opItem }}/GITBOOK_API_TOKEN
|
||||||
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
|
GITBOOK_INTEGRATIONS_HOST: ${{ inputs.opItem }}/GITBOOK_INTEGRATIONS_HOST
|
||||||
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
|
GITBOOK_IMAGE_RESIZE_SIGNING_KEY: ${{ inputs.opItem }}/GITBOOK_IMAGE_RESIZE_SIGNING_KEY
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
.next
|
|
||||||
.vercel
|
|
||||||
.open-next
|
|
||||||
|
|
||||||
# Generated
|
|
||||||
packages/emoji-codepoints/index.ts
|
|
||||||
packages/gitbook/public/~gitbook/static/
|
|
||||||
packages/icons/src/data/*.json
|
|
||||||
|
|
||||||
# Build files
|
|
||||||
dist/
|
|
||||||
|
|
||||||
README.md
|
|
||||||
CHANGELOG.md
|
|
||||||
|
|
||||||
# v2
|
|
||||||
packages/gitbook-v2/public/~gitbook/static/
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"printWidth": 100,
|
|
||||||
"singleQuote": true,
|
|
||||||
"tabWidth": 4
|
|
||||||
}
|
|
||||||
+3
-1
@@ -18,7 +18,9 @@
|
|||||||
"**/.vercel/**/*",
|
"**/.vercel/**/*",
|
||||||
"**/.cache/**/*",
|
"**/.cache/**/*",
|
||||||
"**/.wrangler/**/*",
|
"**/.wrangler/**/*",
|
||||||
"packages/openapi-parser/src/fixtures/**/*"
|
"packages/openapi-parser/src/fixtures/**/*",
|
||||||
|
"packages/emoji-codepoints/index.ts",
|
||||||
|
"packages/icons/src/data/*.json"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
"name": "@gitbook/cache-tags",
|
"name": "@gitbook/cache-tags",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"assert-never": "^1.2.1",
|
"assert-never": "^1.2.1",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -49,9 +49,9 @@
|
|||||||
},
|
},
|
||||||
"packages/gitbook": {
|
"packages/gitbook": {
|
||||||
"name": "gitbook",
|
"name": "gitbook",
|
||||||
"version": "0.7.2",
|
"version": "0.7.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"@gitbook/cache-do": "workspace:*",
|
"@gitbook/cache-do": "workspace:*",
|
||||||
"@gitbook/cache-tags": "workspace:*",
|
"@gitbook/cache-tags": "workspace:*",
|
||||||
"@gitbook/colors": "workspace:*",
|
"@gitbook/colors": "workspace:*",
|
||||||
@@ -134,7 +134,7 @@
|
|||||||
"name": "gitbook-v2",
|
"name": "gitbook-v2",
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"@gitbook/cache-tags": "workspace:*",
|
"@gitbook/cache-tags": "workspace:*",
|
||||||
"@sindresorhus/fnv1a": "^3.1.0",
|
"@sindresorhus/fnv1a": "^3.1.0",
|
||||||
"next": "^15.2.1",
|
"next": "^15.2.1",
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
"warn-once": "^0.1.1",
|
"warn-once": "^0.1.1",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@236c84d",
|
"@opennextjs/cloudflare": "^0.5.10",
|
||||||
"gitbook": "*",
|
"gitbook": "*",
|
||||||
"postcss": "^8",
|
"postcss": "^8",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^3.4.0",
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
},
|
},
|
||||||
"packages/icons": {
|
"packages/icons": {
|
||||||
"name": "@gitbook/icons",
|
"name": "@gitbook/icons",
|
||||||
"version": "0.1.0",
|
"version": "0.2.0",
|
||||||
"bin": {
|
"bin": {
|
||||||
"gitbook-icons": "./bin/gitbook-icons.js",
|
"gitbook-icons": "./bin/gitbook-icons.js",
|
||||||
},
|
},
|
||||||
@@ -194,9 +194,9 @@
|
|||||||
},
|
},
|
||||||
"packages/react-contentkit": {
|
"packages/react-contentkit": {
|
||||||
"name": "@gitbook/react-contentkit",
|
"name": "@gitbook/react-contentkit",
|
||||||
"version": "0.6.1",
|
"version": "0.6.2",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"@gitbook/icons": "workspace:*",
|
"@gitbook/icons": "workspace:*",
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
},
|
},
|
||||||
@@ -226,7 +226,7 @@
|
|||||||
},
|
},
|
||||||
"packages/react-openapi": {
|
"packages/react-openapi": {
|
||||||
"name": "@gitbook/react-openapi",
|
"name": "@gitbook/react-openapi",
|
||||||
"version": "1.1.2",
|
"version": "1.1.3",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/openapi-parser": "workspace:*",
|
"@gitbook/openapi-parser": "workspace:*",
|
||||||
"@scalar/api-client-react": "^1.1.36",
|
"@scalar/api-client-react": "^1.1.36",
|
||||||
@@ -254,6 +254,7 @@
|
|||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"@codemirror/state": "6.4.1",
|
"@codemirror/state": "6.4.1",
|
||||||
|
"@gitbook/api": "0.97.0",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1",
|
"react-dom": "18.3.1",
|
||||||
},
|
},
|
||||||
@@ -626,7 +627,7 @@
|
|||||||
|
|
||||||
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
|
"@fortawesome/fontawesome-svg-core": ["@fortawesome/fontawesome-svg-core@6.6.0", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "6.6.0" } }, "sha512-KHwPkCk6oRT4HADE7smhfsKudt9N/9lm6EJ5BVg0tD1yPA5hht837fB87F8pn15D8JfTqQOjhKTktwmLMiD7Kg=="],
|
||||||
|
|
||||||
"@gitbook/api": ["@gitbook/api@0.96.1", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-E1hpaFNFoEyc8E4rIQyC6UD3kErGXAI0lKt04zYQQYOBLjRIQ57D4oNIIJBjWn4hHeSxRxWvW3NcxzvOGMmIqQ=="],
|
"@gitbook/api": ["@gitbook/api@0.97.0", "", { "dependencies": { "event-iterator": "^2.0.0", "eventsource-parser": "^3.0.0" } }, "sha512-bBTy7nprwlwTW/v8S6A9wGFWatU+F33232xF5Tt4GmSBTZSrAKha5wD205Hr+618va+/zipA8o4hdAGPyUNj0A=="],
|
||||||
|
|
||||||
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
|
"@gitbook/cache-do": ["@gitbook/cache-do@workspace:packages/cache-do"],
|
||||||
|
|
||||||
@@ -792,7 +793,7 @@
|
|||||||
|
|
||||||
"@opennextjs/aws": ["@opennextjs/aws@https://pkg.pr.new/@opennextjs/aws@756", { "dependencies": { "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "esbuild": "0.19.2", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0" }, "bin": { "open-next": "./dist/index.js" } }],
|
"@opennextjs/aws": ["@opennextjs/aws@https://pkg.pr.new/@opennextjs/aws@756", { "dependencies": { "@aws-sdk/client-cloudfront": "3.398.0", "@aws-sdk/client-dynamodb": "^3.398.0", "@aws-sdk/client-lambda": "^3.398.0", "@aws-sdk/client-s3": "^3.398.0", "@aws-sdk/client-sqs": "^3.398.0", "@node-minify/core": "^8.0.6", "@node-minify/terser": "^8.0.6", "@tsconfig/node18": "^1.0.1", "aws4fetch": "^1.0.18", "chalk": "^5.3.0", "esbuild": "0.19.2", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0" }, "bin": { "open-next": "./dist/index.js" } }],
|
||||||
|
|
||||||
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@236c84d", { "dependencies": { "@ast-grep/napi": "^0.34.1", "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@756", "enquirer": "^2.4.1", "glob": "^11.0.0", "yaml": "^2.7.0" }, "peerDependencies": { "wrangler": "^3.111.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }],
|
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@0.5.10", "", { "dependencies": { "@ast-grep/napi": "^0.34.1", "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@756", "enquirer": "^2.4.1", "glob": "^11.0.0", "yaml": "^2.7.0" }, "peerDependencies": { "wrangler": "^3.111.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-L/D472YT5OW1LwpFtD/aVXHJYcVPbFVX7XdphlUjCR4+2osSQIDnsuNgfDRydHMDJZMKxeZDc251ZBzUVKpCqw=="],
|
||||||
|
|
||||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -11,7 +11,8 @@
|
|||||||
"overrides": {
|
"overrides": {
|
||||||
"@codemirror/state": "6.4.1",
|
"@codemirror/state": "6.4.1",
|
||||||
"react": "18.3.1",
|
"react": "18.3.1",
|
||||||
"react-dom": "18.3.1"
|
"react-dom": "18.3.1",
|
||||||
|
"@gitbook/api": "0.97.0"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"assert-never": "^1.2.1"
|
"assert-never": "^1.2.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -111,6 +111,17 @@ export function getCacheTag(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the cache tag for a given URL.
|
||||||
|
*/
|
||||||
|
export function getCacheTagForURL(url: string | URL) {
|
||||||
|
const parsedURL = url instanceof URL ? url : new URL(url);
|
||||||
|
return getCacheTag({
|
||||||
|
tag: 'url',
|
||||||
|
hostname: parsedURL.hostname,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the tags for a computed content source.
|
* Get the tags for a computed content source.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"next": "^15.2.1",
|
"next": "^15.2.1",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"@gitbook/cache-tags": "workspace:*",
|
"@gitbook/cache-tags": "workspace:*",
|
||||||
"@sindresorhus/fnv1a": "^3.1.0",
|
"@sindresorhus/fnv1a": "^3.1.0",
|
||||||
"server-only": "^0.0.1",
|
"server-only": "^0.0.1",
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gitbook": "*",
|
"gitbook": "*",
|
||||||
"@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@236c84d",
|
"@opennextjs/cloudflare": "^0.5.10",
|
||||||
"tailwindcss": "^3.4.0",
|
"tailwindcss": "^3.4.0",
|
||||||
"postcss": "^8"
|
"postcss": "^8"
|
||||||
},
|
},
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
"start": "next start",
|
"start": "next start",
|
||||||
"build:v2:cloudflare": "opennextjs-cloudflare",
|
"build:v2:cloudflare": "opennextjs-cloudflare",
|
||||||
"dev:v2:cloudflare": "wrangler dev --port 8771",
|
"dev:v2:cloudflare": "wrangler dev --port 8771",
|
||||||
"unit": "bun test"
|
"unit": "bun test",
|
||||||
|
"typecheck": "tsc --noEmit"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { type NextRequest, NextResponse } from 'next/server';
|
import { type NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
GITBOOK_API_PUBLIC_URL,
|
||||||
GITBOOK_API_TOKEN,
|
GITBOOK_API_TOKEN,
|
||||||
GITBOOK_API_URL,
|
GITBOOK_API_URL,
|
||||||
GITBOOK_APP_URL,
|
GITBOOK_APP_URL,
|
||||||
@@ -22,6 +23,7 @@ export async function GET(_req: NextRequest) {
|
|||||||
GITBOOK_URL,
|
GITBOOK_URL,
|
||||||
GITBOOK_APP_URL,
|
GITBOOK_APP_URL,
|
||||||
GITBOOK_API_URL,
|
GITBOOK_API_URL,
|
||||||
|
GITBOOK_API_PUBLIC_URL,
|
||||||
GITBOOK_ASSETS_URL,
|
GITBOOK_ASSETS_URL,
|
||||||
GITBOOK_ICONS_URL,
|
GITBOOK_ICONS_URL,
|
||||||
GITBOOK_USER_AGENT,
|
GITBOOK_USER_AGENT,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
fetchSpaceContextByIds,
|
fetchSpaceContextByIds,
|
||||||
} from '@v2/lib/context';
|
} from '@v2/lib/context';
|
||||||
import { createDataFetcher } from '@v2/lib/data';
|
import { createDataFetcher } from '@v2/lib/data';
|
||||||
import { GITBOOK_API_URL } from '@v2/lib/env';
|
|
||||||
import { createLinker } from '@v2/lib/links';
|
import { createLinker } from '@v2/lib/links';
|
||||||
import { getAPITokenFromMiddleware } from '@v2/lib/middleware';
|
import { getAPITokenFromMiddleware } from '@v2/lib/middleware';
|
||||||
|
|
||||||
@@ -26,7 +25,6 @@ export async function getSpacePDFContext(
|
|||||||
});
|
});
|
||||||
const dataFetcher = createDataFetcher({
|
const dataFetcher = createDataFetcher({
|
||||||
apiToken: apiToken,
|
apiToken: apiToken,
|
||||||
apiEndpoint: GITBOOK_API_URL,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const baseContext: GitBookBaseContext = {
|
const baseContext: GitBookBaseContext = {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import type {
|
|||||||
import { type GitBookDataFetcher, createDataFetcher, throwIfDataError } from '@v2/lib/data';
|
import { type GitBookDataFetcher, createDataFetcher, throwIfDataError } from '@v2/lib/data';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
import { assert } from 'ts-essentials';
|
import { assert } from 'ts-essentials';
|
||||||
import { GITBOOK_API_TOKEN, GITBOOK_API_URL, GITBOOK_URL } from './env';
|
import { GITBOOK_URL } from './env';
|
||||||
import { type ImageResizer, createImageResizer } from './images';
|
import { type ImageResizer, createImageResizer } from './images';
|
||||||
import { type GitBookSpaceLinker, createLinker } from './links';
|
import { type GitBookSpaceLinker, createLinker } from './links';
|
||||||
|
|
||||||
@@ -113,8 +113,7 @@ export function getBaseContext(input: {
|
|||||||
const urlMode = input.urlMode;
|
const urlMode = input.urlMode;
|
||||||
|
|
||||||
const dataFetcher = createDataFetcher({
|
const dataFetcher = createDataFetcher({
|
||||||
apiToken: input.apiToken ?? GITBOOK_API_TOKEN,
|
apiToken: input.apiToken ?? null,
|
||||||
apiEndpoint: GITBOOK_API_URL,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const linker = getLinkerForSiteURL({
|
const linker = getLinkerForSiteURL({
|
||||||
|
|||||||
@@ -1,42 +1,40 @@
|
|||||||
import { type ComputedContentSource, GitBookAPI } from '@gitbook/api';
|
import { trace } from '@/lib/tracing';
|
||||||
import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags';
|
import {
|
||||||
|
type ComputedContentSource,
|
||||||
|
GitBookAPI,
|
||||||
|
type GitBookAPIServiceBinding,
|
||||||
|
} from '@gitbook/api';
|
||||||
|
import {
|
||||||
|
getCacheTag,
|
||||||
|
getCacheTagForURL,
|
||||||
|
getComputedContentSourceCacheTags,
|
||||||
|
} from '@gitbook/cache-tags';
|
||||||
import { GITBOOK_API_TOKEN, GITBOOK_API_URL, GITBOOK_USER_AGENT } from '@v2/lib/env';
|
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 { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache';
|
||||||
import { wrapDataFetcherError } from './errors';
|
import { wrapDataFetcherError } from './errors';
|
||||||
import type { GitBookDataFetcher } from './types';
|
import type { GitBookDataFetcher } from './types';
|
||||||
|
|
||||||
interface DataFetcherInput {
|
interface DataFetcherInput {
|
||||||
/**
|
|
||||||
* API host to use.
|
|
||||||
*/
|
|
||||||
apiEndpoint: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* API token.
|
* API token.
|
||||||
*/
|
*/
|
||||||
apiToken: string | null;
|
apiToken: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const commonInput: DataFetcherInput = {
|
|
||||||
apiEndpoint: GITBOOK_API_URL,
|
|
||||||
apiToken: GITBOOK_API_TOKEN,
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a data fetcher using an API token.
|
* Create a data fetcher using an API token.
|
||||||
* The data are being cached by Next.js built-in cache.
|
* The data are being cached by Next.js built-in cache.
|
||||||
*/
|
*/
|
||||||
export function createDataFetcher(input: DataFetcherInput = commonInput): GitBookDataFetcher {
|
export function createDataFetcher(
|
||||||
|
input: DataFetcherInput = { apiToken: null }
|
||||||
|
): GitBookDataFetcher {
|
||||||
return {
|
return {
|
||||||
apiEndpoint: input.apiEndpoint,
|
|
||||||
|
|
||||||
async api() {
|
async api() {
|
||||||
return getAPI(input);
|
return apiClient(input);
|
||||||
},
|
},
|
||||||
|
|
||||||
withToken({ apiToken }) {
|
withToken({ apiToken }) {
|
||||||
return createDataFetcher({
|
return createDataFetcher({
|
||||||
...input,
|
|
||||||
apiToken,
|
apiToken,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@@ -45,94 +43,120 @@ export function createDataFetcher(input: DataFetcherInput = commonInput): GitBoo
|
|||||||
// API that are tied to the token
|
// API that are tied to the token
|
||||||
//
|
//
|
||||||
getPublishedContentSite(params) {
|
getPublishedContentSite(params) {
|
||||||
return getPublishedContentSite(input, {
|
return trace('getPublishedContentSite', () =>
|
||||||
|
getPublishedContentSite(input, {
|
||||||
organizationId: params.organizationId,
|
organizationId: params.organizationId,
|
||||||
siteId: params.siteId,
|
siteId: params.siteId,
|
||||||
siteShareKey: params.siteShareKey,
|
siteShareKey: params.siteShareKey,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getSiteRedirectBySource(params) {
|
getSiteRedirectBySource(params) {
|
||||||
return getSiteRedirectBySource(input, {
|
return trace('getSiteRedirectBySource', () =>
|
||||||
|
getSiteRedirectBySource(input, {
|
||||||
organizationId: params.organizationId,
|
organizationId: params.organizationId,
|
||||||
siteId: params.siteId,
|
siteId: params.siteId,
|
||||||
siteShareKey: params.siteShareKey,
|
siteShareKey: params.siteShareKey,
|
||||||
source: params.source,
|
source: params.source,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getRevision(params) {
|
getRevision(params) {
|
||||||
return getRevision(input, {
|
return trace('getRevision', () =>
|
||||||
|
getRevision(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
revisionId: params.revisionId,
|
revisionId: params.revisionId,
|
||||||
metadata: params.metadata,
|
metadata: params.metadata,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getRevisionPages(params) {
|
getRevisionPages(params) {
|
||||||
return getRevisionPages(input, {
|
return trace('getRevisionPages', () =>
|
||||||
|
getRevisionPages(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
revisionId: params.revisionId,
|
revisionId: params.revisionId,
|
||||||
metadata: params.metadata,
|
metadata: params.metadata,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getRevisionFile(params) {
|
getRevisionFile(params) {
|
||||||
return getRevisionFile(input, {
|
return trace('getRevisionFile', () =>
|
||||||
|
getRevisionFile(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
revisionId: params.revisionId,
|
revisionId: params.revisionId,
|
||||||
fileId: params.fileId,
|
fileId: params.fileId,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getRevisionPageByPath(params) {
|
getRevisionPageByPath(params) {
|
||||||
return getRevisionPageByPath(input, {
|
return trace('getRevisionPageByPath', () =>
|
||||||
|
getRevisionPageByPath(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
revisionId: params.revisionId,
|
revisionId: params.revisionId,
|
||||||
path: params.path,
|
path: params.path,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getReusableContent(params) {
|
getReusableContent(params) {
|
||||||
return getReusableContent(input, {
|
return trace('getReusableContent', () =>
|
||||||
|
getReusableContent(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
revisionId: params.revisionId,
|
revisionId: params.revisionId,
|
||||||
reusableContentId: params.reusableContentId,
|
reusableContentId: params.reusableContentId,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getLatestOpenAPISpecVersionContent(params) {
|
getLatestOpenAPISpecVersionContent(params) {
|
||||||
return getLatestOpenAPISpecVersionContent(input, {
|
return trace('getLatestOpenAPISpecVersionContent', () =>
|
||||||
|
getLatestOpenAPISpecVersionContent(input, {
|
||||||
organizationId: params.organizationId,
|
organizationId: params.organizationId,
|
||||||
slug: params.slug,
|
slug: params.slug,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getSpace(params) {
|
getSpace(params) {
|
||||||
return getSpace(input, {
|
return trace('getSpace', () =>
|
||||||
|
getSpace(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
shareKey: params.shareKey,
|
shareKey: params.shareKey,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getChangeRequest(params) {
|
getChangeRequest(params) {
|
||||||
return getChangeRequest(input, {
|
return trace('getChangeRequest', () =>
|
||||||
|
getChangeRequest(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
changeRequestId: params.changeRequestId,
|
changeRequestId: params.changeRequestId,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getDocument(params) {
|
getDocument(params) {
|
||||||
return getDocument(input, {
|
return trace('getDocument', () =>
|
||||||
|
getDocument(input, {
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
documentId: params.documentId,
|
documentId: params.documentId,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getComputedDocument(params) {
|
getComputedDocument(params) {
|
||||||
return getComputedDocument(input, {
|
return trace('getComputedDocument', () =>
|
||||||
|
getComputedDocument(input, {
|
||||||
organizationId: params.organizationId,
|
organizationId: params.organizationId,
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
source: params.source,
|
source: params.source,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
getEmbedByUrl(params) {
|
getEmbedByUrl(params) {
|
||||||
return getEmbedByUrl(input, {
|
return trace('getEmbedByUrl', () =>
|
||||||
|
getEmbedByUrl(input, {
|
||||||
url: params.url,
|
url: params.url,
|
||||||
spaceId: params.spaceId,
|
spaceId: params.spaceId,
|
||||||
});
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
searchSiteContent(params) {
|
searchSiteContent(params) {
|
||||||
return searchSiteContent(input, params);
|
return trace('searchSiteContent', () => searchSiteContent(input, params));
|
||||||
},
|
},
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -140,25 +164,31 @@ export function createDataFetcher(input: DataFetcherInput = commonInput): GitBoo
|
|||||||
// where the data is the same for all users
|
// where the data is the same for all users
|
||||||
//
|
//
|
||||||
getUserById(userId) {
|
getUserById(userId) {
|
||||||
return getUserById(commonInput, userId);
|
return trace('getUserById', () => getUserById({ apiToken: null }, { userId }));
|
||||||
},
|
},
|
||||||
getPublishedContentByUrl(params) {
|
getPublishedContentByUrl(params) {
|
||||||
return getPublishedContentByUrl(commonInput, {
|
return trace('getPublishedContentByUrl', () =>
|
||||||
|
getPublishedContentByUrl(
|
||||||
|
{ apiToken: null },
|
||||||
|
{
|
||||||
url: params.url,
|
url: params.url,
|
||||||
visitorAuthToken: params.visitorAuthToken,
|
visitorAuthToken: params.visitorAuthToken,
|
||||||
redirectOnError: params.redirectOnError,
|
redirectOnError: params.redirectOnError,
|
||||||
});
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getUserById(input: DataFetcherInput, userId: string) {
|
async function getUserById(input: DataFetcherInput, params: { userId: string }) {
|
||||||
'use cache';
|
'use cache';
|
||||||
|
|
||||||
cacheLife('days');
|
cacheLife('days');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).users.getUserById(userId);
|
const api = await apiClient(input);
|
||||||
|
const res = await api.users.getUserById(params.userId);
|
||||||
return res.data;
|
return res.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -181,7 +211,8 @@ async function getSpace(
|
|||||||
);
|
);
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getSpaceById(params.spaceId, {
|
const api = await apiClient(input);
|
||||||
|
const res = await api.spaces.getSpaceById(params.spaceId, {
|
||||||
shareKey: params.shareKey,
|
shareKey: params.shareKey,
|
||||||
});
|
});
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -200,10 +231,8 @@ async function getChangeRequest(
|
|||||||
cacheLife('minutes');
|
cacheLife('minutes');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getChangeRequestById(
|
const api = await apiClient(input);
|
||||||
params.spaceId,
|
const res = await api.spaces.getChangeRequestById(params.spaceId, params.changeRequestId);
|
||||||
params.changeRequestId
|
|
||||||
);
|
|
||||||
cacheTag(
|
cacheTag(
|
||||||
getCacheTag({
|
getCacheTag({
|
||||||
tag: 'change-request',
|
tag: 'change-request',
|
||||||
@@ -228,7 +257,8 @@ async function getRevision(
|
|||||||
cacheLife('max');
|
cacheLife('max');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getRevisionById(params.spaceId, params.revisionId, {
|
const api = await apiClient(input);
|
||||||
|
const res = await api.spaces.getRevisionById(params.spaceId, params.revisionId, {
|
||||||
metadata: params.metadata,
|
metadata: params.metadata,
|
||||||
});
|
});
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -248,13 +278,10 @@ async function getRevisionPages(
|
|||||||
cacheLife('max');
|
cacheLife('max');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.listPagesInRevisionById(
|
const api = await apiClient(input);
|
||||||
params.spaceId,
|
const res = await api.spaces.listPagesInRevisionById(params.spaceId, params.revisionId, {
|
||||||
params.revisionId,
|
|
||||||
{
|
|
||||||
metadata: params.metadata,
|
metadata: params.metadata,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
return res.data.pages;
|
return res.data.pages;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -272,10 +299,12 @@ async function getRevisionFile(
|
|||||||
cacheLife('max');
|
cacheLife('max');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getFileInRevisionById(
|
const api = await apiClient(input);
|
||||||
|
const res = await api.spaces.getFileInRevisionById(
|
||||||
params.spaceId,
|
params.spaceId,
|
||||||
params.revisionId,
|
params.revisionId,
|
||||||
params.fileId
|
params.fileId,
|
||||||
|
{}
|
||||||
);
|
);
|
||||||
return res.data;
|
return res.data;
|
||||||
});
|
});
|
||||||
@@ -295,10 +324,12 @@ async function getRevisionPageByPath(
|
|||||||
|
|
||||||
const encodedPath = encodeURIComponent(params.path);
|
const encodedPath = encodeURIComponent(params.path);
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getPageInRevisionByPath(
|
const api = await apiClient(input);
|
||||||
|
const res = await api.spaces.getPageInRevisionByPath(
|
||||||
params.spaceId,
|
params.spaceId,
|
||||||
params.revisionId,
|
params.revisionId,
|
||||||
encodedPath
|
encodedPath,
|
||||||
|
{}
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -317,7 +348,8 @@ async function getDocument(
|
|||||||
cacheLife('max');
|
cacheLife('max');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getDocumentById(params.spaceId, params.documentId);
|
const api = await apiClient(input);
|
||||||
|
const res = await api.spaces.getDocumentById(params.spaceId, params.documentId, {});
|
||||||
return res.data;
|
return res.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -345,7 +377,8 @@ async function getComputedDocument(
|
|||||||
);
|
);
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getComputedDocument(params.spaceId, {
|
const api = await apiClient(input);
|
||||||
|
const res = await api.spaces.getComputedDocument(params.spaceId, {
|
||||||
source: params.source,
|
source: params.source,
|
||||||
});
|
});
|
||||||
return res.data;
|
return res.data;
|
||||||
@@ -365,7 +398,8 @@ async function getReusableContent(
|
|||||||
cacheLife('max');
|
cacheLife('max');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).spaces.getReusableContentInRevisionById(
|
const api = await apiClient(input);
|
||||||
|
const res = await api.spaces.getReusableContentInRevisionById(
|
||||||
params.spaceId,
|
params.spaceId,
|
||||||
params.revisionId,
|
params.revisionId,
|
||||||
params.reusableContentId
|
params.reusableContentId
|
||||||
@@ -393,7 +427,8 @@ async function getLatestOpenAPISpecVersionContent(
|
|||||||
cacheLife('days');
|
cacheLife('days');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).orgs.getLatestOpenApiSpecVersionContent(
|
const api = await apiClient(input);
|
||||||
|
const res = await api.orgs.getLatestOpenApiSpecVersionContent(
|
||||||
params.organizationId,
|
params.organizationId,
|
||||||
params.slug
|
params.slug
|
||||||
);
|
);
|
||||||
@@ -413,17 +448,12 @@ async function getPublishedContentByUrl(
|
|||||||
|
|
||||||
const { url, visitorAuthToken, redirectOnError } = params;
|
const { url, visitorAuthToken, redirectOnError } = params;
|
||||||
|
|
||||||
const hostname = new URL(url).hostname;
|
cacheTag(getCacheTagForURL(url));
|
||||||
cacheTag(
|
|
||||||
getCacheTag({
|
|
||||||
tag: 'url',
|
|
||||||
hostname,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
cacheLife('days');
|
cacheLife('days');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).urls.getPublishedContentByUrl({
|
const api = await apiClient(input);
|
||||||
|
const res = await api.urls.getPublishedContentByUrl({
|
||||||
url,
|
url,
|
||||||
visitorAuthToken: visitorAuthToken ?? undefined,
|
visitorAuthToken: visitorAuthToken ?? undefined,
|
||||||
redirectOnError,
|
redirectOnError,
|
||||||
@@ -452,22 +482,19 @@ async function getPublishedContentSite(
|
|||||||
) {
|
) {
|
||||||
'use cache';
|
'use cache';
|
||||||
|
|
||||||
|
cacheLife('days');
|
||||||
cacheTag(
|
cacheTag(
|
||||||
getCacheTag({
|
getCacheTag({
|
||||||
tag: 'site',
|
tag: 'site',
|
||||||
site: params.siteId,
|
site: params.siteId,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
cacheLife('days');
|
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).orgs.getPublishedContentSite(
|
const api = await apiClient(input);
|
||||||
params.organizationId,
|
const res = await api.orgs.getPublishedContentSite(params.organizationId, params.siteId, {
|
||||||
params.siteId,
|
|
||||||
{
|
|
||||||
shareKey: params.siteShareKey,
|
shareKey: params.siteShareKey,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
return res.data;
|
return res.data;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -492,14 +519,11 @@ async function getSiteRedirectBySource(
|
|||||||
cacheLife('days');
|
cacheLife('days');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).orgs.getSiteRedirectBySource(
|
const api = await apiClient(input);
|
||||||
params.organizationId,
|
const res = await api.orgs.getSiteRedirectBySource(params.organizationId, params.siteId, {
|
||||||
params.siteId,
|
|
||||||
{
|
|
||||||
shareKey: params.siteShareKey,
|
shareKey: params.siteShareKey,
|
||||||
source: params.source,
|
source: params.source,
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
return res.data;
|
return res.data;
|
||||||
});
|
});
|
||||||
@@ -517,7 +541,7 @@ async function getEmbedByUrl(
|
|||||||
cacheLife('weeks');
|
cacheLife('weeks');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const api = getAPI(input);
|
const api = await apiClient(input);
|
||||||
const res = await api.spaces.getEmbedByUrlInSpace(params.spaceId, { url: params.url });
|
const res = await api.spaces.getEmbedByUrlInSpace(params.spaceId, { url: params.url });
|
||||||
return res.data;
|
return res.data;
|
||||||
});
|
});
|
||||||
@@ -534,7 +558,8 @@ async function searchSiteContent(
|
|||||||
cacheLife('days');
|
cacheLife('days');
|
||||||
|
|
||||||
return wrapDataFetcherError(async () => {
|
return wrapDataFetcherError(async () => {
|
||||||
const res = await getAPI(input).orgs.searchSiteContent(organizationId, siteId, {
|
const api = await apiClient(input);
|
||||||
|
const res = await api.orgs.searchSiteContent(organizationId, siteId, {
|
||||||
query,
|
query,
|
||||||
...scope,
|
...scope,
|
||||||
});
|
});
|
||||||
@@ -542,12 +567,43 @@ async function searchSiteContent(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAPI(input: DataFetcherInput) {
|
let loggedServiceBinding = false;
|
||||||
const { apiEndpoint, apiToken } = input;
|
|
||||||
|
/**
|
||||||
|
* Create a new API client.
|
||||||
|
*/
|
||||||
|
export async function apiClient(input: DataFetcherInput = { apiToken: null }) {
|
||||||
|
const { apiToken } = input;
|
||||||
|
let serviceBinding: GitBookAPIServiceBinding | undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// HACK: This is a workaround to avoid webpack trying to bundle this cloudflare only module
|
||||||
|
// @ts-ignore
|
||||||
|
const { env } = await import(
|
||||||
|
/* webpackIgnore: true */ `${'__cloudflare:workers'.replaceAll('_', '')}`
|
||||||
|
);
|
||||||
|
serviceBinding = env.GITBOOK_API;
|
||||||
|
if (!loggedServiceBinding) {
|
||||||
|
loggedServiceBinding = true;
|
||||||
|
if (serviceBinding) {
|
||||||
|
// biome-ignore lint/suspicious/noConsole: we want to log here
|
||||||
|
console.log(`using service binding for the API (${GITBOOK_API_URL})`);
|
||||||
|
} else {
|
||||||
|
// biome-ignore lint/suspicious/noConsole: we want to log here
|
||||||
|
console.warn(`no service binding for the API (${GITBOOK_API_URL})`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if (process.env.NODE_ENV === 'production' && !process.env.VERCEL) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const api = new GitBookAPI({
|
const api = new GitBookAPI({
|
||||||
authToken: apiToken ?? undefined,
|
authToken: apiToken || GITBOOK_API_TOKEN || undefined,
|
||||||
endpoint: apiEndpoint,
|
endpoint: GITBOOK_API_URL,
|
||||||
userAgent: GITBOOK_USER_AGENT,
|
userAgent: GITBOOK_USER_AGENT,
|
||||||
|
serviceBinding,
|
||||||
});
|
});
|
||||||
|
|
||||||
return api;
|
return api;
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { race, tryCatch } from '@/lib/async';
|
import { race, tryCatch } from '@/lib/async';
|
||||||
import { joinPath } from '@/lib/paths';
|
import { joinPath } from '@/lib/paths';
|
||||||
import { GitBookAPI, type PublishedSiteContentLookup } from '@gitbook/api';
|
import { trace } from '@/lib/tracing';
|
||||||
import { GITBOOK_API_TOKEN, GITBOOK_API_URL, GITBOOK_USER_AGENT } from '@v2/lib/env';
|
import type { PublishedSiteContentLookup } from '@gitbook/api';
|
||||||
|
import { apiClient } from './api';
|
||||||
import { getExposableError } from './errors';
|
import { getExposableError } from './errors';
|
||||||
import type { DataFetcherResponse } from './types';
|
import type { DataFetcherResponse } from './types';
|
||||||
import { getURLLookupAlternatives, stripURLSearch } from './urls';
|
import { getURLLookupAlternatives, stripURLSearch } from './urls';
|
||||||
@@ -20,34 +21,26 @@ export async function getPublishedContentByURL(input: {
|
|||||||
const lookup = getURLLookupAlternatives(url);
|
const lookup = getURLLookupAlternatives(url);
|
||||||
|
|
||||||
const result = await race(lookup.urls, async (alternative, { signal }) => {
|
const result = await race(lookup.urls, async (alternative, { signal }) => {
|
||||||
const api = new GitBookAPI({
|
const api = await apiClient();
|
||||||
authToken: GITBOOK_API_TOKEN ?? undefined,
|
|
||||||
endpoint: GITBOOK_API_URL,
|
|
||||||
userAgent: GITBOOK_USER_AGENT,
|
|
||||||
});
|
|
||||||
|
|
||||||
const startTime = performance.now();
|
const callResult = await trace(
|
||||||
const callResult = await tryCatch(
|
{
|
||||||
|
operation: 'getPublishedContentByURL',
|
||||||
|
name: alternative.url,
|
||||||
|
},
|
||||||
|
() =>
|
||||||
|
tryCatch(
|
||||||
api.urls.getPublishedContentByUrl(
|
api.urls.getPublishedContentByUrl(
|
||||||
{
|
{
|
||||||
url: alternative.url,
|
url: alternative.url,
|
||||||
visitorAuthToken: input.visitorAuthToken ?? undefined,
|
visitorAuthToken: input.visitorAuthToken ?? undefined,
|
||||||
redirectOnError: input.redirectOnError,
|
redirectOnError: input.redirectOnError,
|
||||||
cache: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
signal,
|
signal,
|
||||||
headers: {
|
|
||||||
'x-gitbook-force-cache': 'true',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
)
|
||||||
const endTime = performance.now();
|
|
||||||
|
|
||||||
// biome-ignore lint/suspicious/noConsole: we want to log performance data
|
|
||||||
console.log(
|
|
||||||
`getPublishedContentByURL(${alternative.url}) Time taken: ${endTime - startTime}ms`
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (callResult.error) {
|
if (callResult.error) {
|
||||||
|
|||||||
@@ -20,11 +20,6 @@ export type DataFetcherResponse<T> =
|
|||||||
* It is used between v1 and v2.
|
* It is used between v1 and v2.
|
||||||
*/
|
*/
|
||||||
export interface GitBookDataFetcher {
|
export interface GitBookDataFetcher {
|
||||||
/**
|
|
||||||
* Endpoint of the API.
|
|
||||||
*/
|
|
||||||
apiEndpoint: string;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an API client for the current context.
|
* Get an API client for the current context.
|
||||||
*/
|
*/
|
||||||
|
|||||||
+5
@@ -34,6 +34,11 @@ export const GITBOOK_APP_URL = process.env.GITBOOK_APP_URL || 'https://app.gitbo
|
|||||||
*/
|
*/
|
||||||
export const GITBOOK_API_URL = process.env.GITBOOK_API_URL || 'https://api.gitbook.com';
|
export const GITBOOK_API_URL = process.env.GITBOOK_API_URL || 'https://api.gitbook.com';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default GitBook API URL endpoint, to be shared with the client.
|
||||||
|
*/
|
||||||
|
export const GITBOOK_API_PUBLIC_URL = process.env.GITBOOK_API_PUBLIC_URL || GITBOOK_API_URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default GitBook API token.
|
* Default GitBook API token.
|
||||||
* It can be use to avoid rate-limiting.
|
* It can be use to avoid rate-limiting.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
main = ".open-next/worker.js"
|
main = ".open-next/worker.js"
|
||||||
name = "gitbook-open-v2"
|
name = "gitbook-open-v2"
|
||||||
compatibility_date = "2024-09-23"
|
compatibility_date = "2025-03-11"
|
||||||
compatibility_flags = ["nodejs_compat"]
|
compatibility_flags = ["nodejs_compat", "allow_importable_env"]
|
||||||
assets = { directory = ".open-next/assets", binding = "ASSETS" }
|
assets = { directory = ".open-next/assets", binding = "ASSETS" }
|
||||||
observability = { enabled = true }
|
observability = { enabled = true }
|
||||||
|
|
||||||
@@ -12,6 +12,10 @@ kv_namespaces = [
|
|||||||
d1_databases = [
|
d1_databases = [
|
||||||
{ binding = "NEXT_CACHE_D1", database_id = "f59ddb40-ad72-4312-9395-0ac6a129af8e", database_name = "gitbook-open-v2-tags-preview" }
|
{ binding = "NEXT_CACHE_D1", database_id = "f59ddb40-ad72-4312-9395-0ac6a129af8e", database_name = "gitbook-open-v2-tags-preview" }
|
||||||
]
|
]
|
||||||
|
services = [
|
||||||
|
{ binding = "NEXT_CACHE_REVALIDATION_WORKER", service = "gitbook-open-v2-preview" },
|
||||||
|
{ binding = "GITBOOK_API", service = "gitbook-x-prod-api-cache" }
|
||||||
|
]
|
||||||
|
|
||||||
[env.staging]
|
[env.staging]
|
||||||
routes = [
|
routes = [
|
||||||
@@ -24,6 +28,10 @@ kv_namespaces = [
|
|||||||
d1_databases = [
|
d1_databases = [
|
||||||
{ binding = "NEXT_CACHE_D1", database_id = "9df62e39-1f35-4066-83aa-e9b8ed3ac8d5", database_name = "gitbook-open-v2-tags-staging" }
|
{ binding = "NEXT_CACHE_D1", database_id = "9df62e39-1f35-4066-83aa-e9b8ed3ac8d5", database_name = "gitbook-open-v2-tags-staging" }
|
||||||
]
|
]
|
||||||
|
services = [
|
||||||
|
{ binding = "NEXT_CACHE_REVALIDATION_WORKER", service = "gitbook-open-v2-staging" },
|
||||||
|
{ binding = "GITBOOK_API", service = "gitbook-x-staging-api-cache" }
|
||||||
|
]
|
||||||
|
|
||||||
[env.production]
|
[env.production]
|
||||||
routes = [
|
routes = [
|
||||||
@@ -36,3 +44,7 @@ kv_namespaces = [
|
|||||||
d1_databases = [
|
d1_databases = [
|
||||||
{ binding = "NEXT_CACHE_D1", database_id = "a6f16fce-5f45-43a9-89a4-7b83ddf25b77", database_name = "gitbook-open-v2-tags-production" }
|
{ binding = "NEXT_CACHE_D1", database_id = "a6f16fce-5f45-43a9-89a4-7b83ddf25b77", database_name = "gitbook-open-v2-tags-production" }
|
||||||
]
|
]
|
||||||
|
services = [
|
||||||
|
{ binding = "NEXT_CACHE_REVALIDATION_WORKER", service = "gitbook-open-v2-production" },
|
||||||
|
{ binding = "GITBOOK_API", service = "gitbook-x-prod-api-cache" }
|
||||||
|
]
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { argosScreenshot } from '@argos-ci/playwright';
|
|||||||
import {
|
import {
|
||||||
CustomizationBackground,
|
CustomizationBackground,
|
||||||
CustomizationCorners,
|
CustomizationCorners,
|
||||||
CustomizationFont,
|
CustomizationDefaultFont,
|
||||||
type CustomizationHeaderItem,
|
type CustomizationHeaderItem,
|
||||||
CustomizationHeaderPreset,
|
CustomizationHeaderPreset,
|
||||||
CustomizationIconsStyle,
|
CustomizationIconsStyle,
|
||||||
@@ -249,7 +249,7 @@ export function getCustomizationURL(partial: DeepPartial<SiteCustomizationSettin
|
|||||||
dangerColor: { light: '#FB2C36', dark: '#FB2C36' },
|
dangerColor: { light: '#FB2C36', dark: '#FB2C36' },
|
||||||
successColor: { light: '#00C950', dark: '#00C950' },
|
successColor: { light: '#00C950', dark: '#00C950' },
|
||||||
corners: CustomizationCorners.Rounded,
|
corners: CustomizationCorners.Rounded,
|
||||||
font: CustomizationFont.Inter,
|
font: CustomizationDefaultFont.Inter,
|
||||||
background: CustomizationBackground.Plain,
|
background: CustomizationBackground.Plain,
|
||||||
icons: CustomizationIconsStyle.Regular,
|
icons: CustomizationIconsStyle.Regular,
|
||||||
links: CustomizationLinksStyle.Default,
|
links: CustomizationLinksStyle.Default,
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static/icons && rm -rf ./public/~gitbook/static/math"
|
"clean": "rm -rf ./.next && rm -rf ./public/~gitbook/static/icons && rm -rf ./public/~gitbook/static/math"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"@gitbook/cache-do": "workspace:*",
|
"@gitbook/cache-do": "workspace:*",
|
||||||
"@gitbook/cache-tags": "workspace:*",
|
"@gitbook/cache-tags": "workspace:*",
|
||||||
"@gitbook/colors": "workspace:*",
|
"@gitbook/colors": "workspace:*",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { SpaceLayout } from '@/components/SpaceLayout';
|
|||||||
import { buildVersion } from '@/lib/build';
|
import { buildVersion } from '@/lib/build';
|
||||||
import { isSiteIndexable } from '@/lib/seo';
|
import { isSiteIndexable } from '@/lib/seo';
|
||||||
|
|
||||||
import { GITBOOK_API_URL, GITBOOK_ASSETS_URL } from '@v2/lib/env';
|
import { GITBOOK_API_PUBLIC_URL, GITBOOK_ASSETS_URL, GITBOOK_ICONS_URL } from '@v2/lib/env';
|
||||||
import { ClientContexts } from './ClientContexts';
|
import { ClientContexts } from './ClientContexts';
|
||||||
import { RocketLoaderDetector } from './RocketLoaderDetector';
|
import { RocketLoaderDetector } from './RocketLoaderDetector';
|
||||||
|
|
||||||
@@ -31,7 +31,8 @@ export async function SiteLayout(props: {
|
|||||||
|
|
||||||
const { scripts, customization } = context;
|
const { scripts, customization } = context;
|
||||||
|
|
||||||
ReactDOM.preconnect(GITBOOK_API_URL);
|
ReactDOM.preconnect(GITBOOK_API_PUBLIC_URL);
|
||||||
|
ReactDOM.preconnect(GITBOOK_ICONS_URL);
|
||||||
if (GITBOOK_ASSETS_URL) {
|
if (GITBOOK_ASSETS_URL) {
|
||||||
ReactDOM.preconnect(GITBOOK_ASSETS_URL);
|
ReactDOM.preconnect(GITBOOK_ASSETS_URL);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import { getSpaceLanguage } from '@/intl/server';
|
|||||||
import { t } from '@/intl/translate';
|
import { t } from '@/intl/translate';
|
||||||
import { tcls } from '@/lib/tailwind';
|
import { tcls } from '@/lib/tailwind';
|
||||||
|
|
||||||
import { GITBOOK_APP_URL } from '@v2/lib/env';
|
import { GITBOOK_API_PUBLIC_URL, GITBOOK_APP_URL } from '@v2/lib/env';
|
||||||
import { SpacesDropdown } from '../Header/SpacesDropdown';
|
import { SpacesDropdown } from '../Header/SpacesDropdown';
|
||||||
import { InsightsProvider } from '../Insights';
|
import { InsightsProvider } from '../Insights';
|
||||||
import { SiteSectionList, encodeClientSiteSections } from '../SiteSections';
|
import { SiteSectionList, encodeClientSiteSections } from '../SiteSections';
|
||||||
@@ -51,7 +51,7 @@ export function SpaceLayout(props: {
|
|||||||
<InsightsProvider
|
<InsightsProvider
|
||||||
enabled={withTracking}
|
enabled={withTracking}
|
||||||
appURL={GITBOOK_APP_URL}
|
appURL={GITBOOK_APP_URL}
|
||||||
apiHost={context.dataFetcher.apiEndpoint}
|
apiHost={GITBOOK_API_PUBLIC_URL}
|
||||||
visitorAuthToken={visitorAuthToken}
|
visitorAuthToken={visitorAuthToken}
|
||||||
organizationId={context.organizationId}
|
organizationId={context.organizationId}
|
||||||
siteId={context.site.id}
|
siteId={context.site.id}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CustomizationFont } from '@gitbook/api';
|
import { CustomizationDefaultFont } from '@gitbook/api';
|
||||||
import {
|
import {
|
||||||
Fira_Sans_Extra_Condensed,
|
Fira_Sans_Extra_Condensed,
|
||||||
IBM_Plex_Mono,
|
IBM_Plex_Mono,
|
||||||
@@ -188,20 +188,20 @@ const abcFavorit = localFont({
|
|||||||
/**
|
/**
|
||||||
* Font definitions.
|
* Font definitions.
|
||||||
*/
|
*/
|
||||||
export const fonts: { [fontName in CustomizationFont]: { variable: string } } = {
|
export const fonts: { [fontName in CustomizationDefaultFont]: { variable: string } } = {
|
||||||
[CustomizationFont.Inter]: inter,
|
[CustomizationDefaultFont.Inter]: inter,
|
||||||
[CustomizationFont.FiraSans]: firaSans,
|
[CustomizationDefaultFont.FiraSans]: firaSans,
|
||||||
[CustomizationFont.IBMPlexSerif]: ibmPlexSerif,
|
[CustomizationDefaultFont.IBMPlexSerif]: ibmPlexSerif,
|
||||||
[CustomizationFont.Lato]: lato,
|
[CustomizationDefaultFont.Lato]: lato,
|
||||||
[CustomizationFont.Merriweather]: merriweather,
|
[CustomizationDefaultFont.Merriweather]: merriweather,
|
||||||
[CustomizationFont.NotoSans]: notoSans,
|
[CustomizationDefaultFont.NotoSans]: notoSans,
|
||||||
[CustomizationFont.OpenSans]: openSans,
|
[CustomizationDefaultFont.OpenSans]: openSans,
|
||||||
[CustomizationFont.Overpass]: overpass,
|
[CustomizationDefaultFont.Overpass]: overpass,
|
||||||
[CustomizationFont.Poppins]: poppins,
|
[CustomizationDefaultFont.Poppins]: poppins,
|
||||||
[CustomizationFont.Raleway]: raleway,
|
[CustomizationDefaultFont.Raleway]: raleway,
|
||||||
[CustomizationFont.Roboto]: roboto,
|
[CustomizationDefaultFont.Roboto]: roboto,
|
||||||
[CustomizationFont.RobotoSlab]: robotoSlab,
|
[CustomizationDefaultFont.RobotoSlab]: robotoSlab,
|
||||||
[CustomizationFont.SourceSansPro]: sourceSansPro,
|
[CustomizationDefaultFont.SourceSansPro]: sourceSansPro,
|
||||||
[CustomizationFont.Ubuntu]: ubuntu,
|
[CustomizationDefaultFont.Ubuntu]: ubuntu,
|
||||||
[CustomizationFont.ABCFavorit]: abcFavorit,
|
[CustomizationDefaultFont.ABCFavorit]: abcFavorit,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { AsyncLocalStorage } from 'node:async_hooks';
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
||||||
import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags';
|
import {
|
||||||
|
getCacheTag,
|
||||||
|
getCacheTagForURL,
|
||||||
|
getComputedContentSourceCacheTags,
|
||||||
|
} from '@gitbook/cache-tags';
|
||||||
import 'server-only';
|
import 'server-only';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -238,11 +242,7 @@ export const getLatestOpenAPISpecVersionContent = cache({
|
|||||||
*/
|
*/
|
||||||
export const getPublishedContentByUrl = cache({
|
export const getPublishedContentByUrl = cache({
|
||||||
name: 'api.getPublishedContentByUrl.v4',
|
name: 'api.getPublishedContentByUrl.v4',
|
||||||
tag: (url) =>
|
tag: (url) => getCacheTagForURL(url),
|
||||||
getCacheTag({
|
|
||||||
tag: 'url',
|
|
||||||
hostname: new URL(url).hostname,
|
|
||||||
}),
|
|
||||||
get: async (
|
get: async (
|
||||||
url: string,
|
url: string,
|
||||||
visitorAuthToken: string | undefined,
|
visitorAuthToken: string | undefined,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'bun:test';
|
import { describe, expect, it } from 'bun:test';
|
||||||
|
|
||||||
import { asyncMutexFunction, race } from './async';
|
import { race } from './async';
|
||||||
import { flushWaitUntil } from './waitUntil';
|
import { flushWaitUntil } from './waitUntil';
|
||||||
|
|
||||||
describe('race', () => {
|
describe('race', () => {
|
||||||
@@ -380,88 +380,3 @@ describe('race', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('asyncMutexFunction', () => {
|
|
||||||
describe('self', () => {
|
|
||||||
it('should run only once', async () => {
|
|
||||||
let running = 0;
|
|
||||||
|
|
||||||
const fn = async () => {
|
|
||||||
running += 1;
|
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
||||||
};
|
|
||||||
|
|
||||||
const mutexFn = asyncMutexFunction();
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
mutexFn(fn),
|
|
||||||
mutexFn(fn),
|
|
||||||
mutexFn(fn),
|
|
||||||
mutexFn(fn),
|
|
||||||
mutexFn(fn),
|
|
||||||
mutexFn(fn),
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(running).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('runBlocking', () => {
|
|
||||||
it('should run only one function at a time', async () => {
|
|
||||||
let running = 0;
|
|
||||||
let maxRunning = 0;
|
|
||||||
|
|
||||||
const fn = async () => {
|
|
||||||
running += 1;
|
|
||||||
maxRunning = Math.max(running, maxRunning);
|
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
||||||
|
|
||||||
running -= 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mutexFn = asyncMutexFunction();
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
mutexFn.runBlocking(fn),
|
|
||||||
mutexFn.runBlocking(fn),
|
|
||||||
mutexFn.runBlocking(fn),
|
|
||||||
mutexFn.runBlocking(fn),
|
|
||||||
mutexFn.runBlocking(fn),
|
|
||||||
mutexFn.runBlocking(fn),
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(maxRunning).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('runAfter', () => {
|
|
||||||
it('should run only one function at a time', async () => {
|
|
||||||
let running = 0;
|
|
||||||
let maxRunning = 0;
|
|
||||||
|
|
||||||
const fn = async () => {
|
|
||||||
running += 1;
|
|
||||||
maxRunning = Math.max(running, maxRunning);
|
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
||||||
|
|
||||||
running -= 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
const mutexFn = asyncMutexFunction();
|
|
||||||
|
|
||||||
await Promise.all([
|
|
||||||
mutexFn.runAfter(fn),
|
|
||||||
mutexFn.runAfter(fn),
|
|
||||||
mutexFn.runAfter(fn),
|
|
||||||
mutexFn.runAfter(fn),
|
|
||||||
mutexFn.runAfter(fn),
|
|
||||||
mutexFn.runAfter(fn),
|
|
||||||
]);
|
|
||||||
|
|
||||||
expect(maxRunning).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -388,87 +388,6 @@ export type AsyncMutexFunction<T> = ((fn: () => Promise<T>) => Promise<T>) & {
|
|||||||
) => Promise<ReturnType>;
|
) => Promise<ReturnType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a function that will only call the given function once at a time.
|
|
||||||
*/
|
|
||||||
export function asyncMutexFunction<T>(): AsyncMutexFunction<T> {
|
|
||||||
let pending:
|
|
||||||
| undefined
|
|
||||||
| { kind: 'value'; promise: Promise<T> }
|
|
||||||
| { kind: 'blocking'; promise: Promise<unknown> };
|
|
||||||
|
|
||||||
const mutex: AsyncMutexFunction<T> = async (fn) => {
|
|
||||||
if (pending?.kind === 'value') {
|
|
||||||
return pending.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (pending) {
|
|
||||||
await pending.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = fn();
|
|
||||||
pending = { kind: 'value', promise };
|
|
||||||
try {
|
|
||||||
const result = await promise;
|
|
||||||
return result;
|
|
||||||
} finally {
|
|
||||||
pending = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mutex.wait = async () => {
|
|
||||||
return pending?.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
mutex.runBlocking = async (fn, options) => {
|
|
||||||
const failOnMutexError = options?.failOnMutexError ?? true;
|
|
||||||
|
|
||||||
while (pending) {
|
|
||||||
try {
|
|
||||||
await pending.promise;
|
|
||||||
} catch (err) {
|
|
||||||
if (failOnMutexError) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = fn();
|
|
||||||
pending = { kind: 'blocking', promise };
|
|
||||||
try {
|
|
||||||
const result = await promise;
|
|
||||||
return result;
|
|
||||||
} finally {
|
|
||||||
pending = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mutex.runAfter = async (fn, options) => {
|
|
||||||
const failOnMutexError = options?.failOnMutexError ?? true;
|
|
||||||
|
|
||||||
while (pending) {
|
|
||||||
try {
|
|
||||||
await pending.promise;
|
|
||||||
} catch (err) {
|
|
||||||
if (failOnMutexError) {
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const promise = fn();
|
|
||||||
pending = { kind: 'value', promise };
|
|
||||||
try {
|
|
||||||
const result = await pending.promise;
|
|
||||||
return result;
|
|
||||||
} finally {
|
|
||||||
pending = undefined;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return mutex;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Try catch a promise and return the result or the error.
|
* Try catch a promise and return the result or the error.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { startSpan } from '@sentry/nextjs';
|
|
||||||
|
|
||||||
export interface TraceSpan {
|
export interface TraceSpan {
|
||||||
setAttribute: (label: string, value: boolean | string | number) => void;
|
setAttribute: (label: string, value: boolean | string | number) => void;
|
||||||
}
|
}
|
||||||
@@ -22,17 +20,10 @@ export async function trace<T>(
|
|||||||
typeof name === 'string' ? { operation: name, name: undefined } : name;
|
typeof name === 'string' ? { operation: name, name: undefined } : name;
|
||||||
const completeName = executionName ? `${operation}(${executionName})` : operation;
|
const completeName = executionName ? `${operation}(${executionName})` : operation;
|
||||||
|
|
||||||
return await startSpan(
|
|
||||||
{
|
|
||||||
name: completeName,
|
|
||||||
op: operation,
|
|
||||||
},
|
|
||||||
async (sentrySpan) => {
|
|
||||||
const attributes: Record<string, boolean | string | number> = {};
|
const attributes: Record<string, boolean | string | number> = {};
|
||||||
const span: TraceSpan = {
|
const span: TraceSpan = {
|
||||||
setAttribute(label, value) {
|
setAttribute(label, value) {
|
||||||
attributes[label] = value;
|
attributes[label] = value;
|
||||||
sentrySpan?.setAttribute(label, value);
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,8 +41,6 @@ export async function trace<T>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the current time in milliseconds.
|
* Return the current time in milliseconds.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export function defaultCustomizationForSpace(): api.CustomizationSettings {
|
|||||||
light: '#346DDB',
|
light: '#346DDB',
|
||||||
},
|
},
|
||||||
corners: api.CustomizationCorners.Rounded,
|
corners: api.CustomizationCorners.Rounded,
|
||||||
font: api.CustomizationFont.Inter,
|
font: api.CustomizationDefaultFont.Inter,
|
||||||
background: api.CustomizationBackground.Plain,
|
background: api.CustomizationBackground.Plain,
|
||||||
},
|
},
|
||||||
favicon: {},
|
favicon: {},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { CustomizationFont, CustomizationHeaderPreset } from '@gitbook/api';
|
import { CustomizationDefaultFont, CustomizationHeaderPreset } from '@gitbook/api';
|
||||||
import { colorContrast } from '@gitbook/colors';
|
import { colorContrast } from '@gitbook/colors';
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
import { ImageResponse } from 'next/og';
|
import { ImageResponse } from 'next/og';
|
||||||
@@ -8,22 +8,22 @@ import { getAssetURL } from '@/lib/assets';
|
|||||||
import { filterOutNullable } from '@/lib/typescript';
|
import { filterOutNullable } from '@/lib/typescript';
|
||||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||||
|
|
||||||
const googleFontsMap: { [fontName in CustomizationFont]: string } = {
|
const googleFontsMap: { [fontName in CustomizationDefaultFont]: string } = {
|
||||||
[CustomizationFont.Inter]: 'Inter',
|
[CustomizationDefaultFont.Inter]: 'Inter',
|
||||||
[CustomizationFont.FiraSans]: 'Fira Sans Extra Condensed',
|
[CustomizationDefaultFont.FiraSans]: 'Fira Sans Extra Condensed',
|
||||||
[CustomizationFont.IBMPlexSerif]: 'IBM Plex Serif',
|
[CustomizationDefaultFont.IBMPlexSerif]: 'IBM Plex Serif',
|
||||||
[CustomizationFont.Lato]: 'Lato',
|
[CustomizationDefaultFont.Lato]: 'Lato',
|
||||||
[CustomizationFont.Merriweather]: 'Merriweather',
|
[CustomizationDefaultFont.Merriweather]: 'Merriweather',
|
||||||
[CustomizationFont.NotoSans]: 'Noto Sans',
|
[CustomizationDefaultFont.NotoSans]: 'Noto Sans',
|
||||||
[CustomizationFont.OpenSans]: 'Open Sans',
|
[CustomizationDefaultFont.OpenSans]: 'Open Sans',
|
||||||
[CustomizationFont.Overpass]: 'Overpass',
|
[CustomizationDefaultFont.Overpass]: 'Overpass',
|
||||||
[CustomizationFont.Poppins]: 'Poppins',
|
[CustomizationDefaultFont.Poppins]: 'Poppins',
|
||||||
[CustomizationFont.Raleway]: 'Raleway',
|
[CustomizationDefaultFont.Raleway]: 'Raleway',
|
||||||
[CustomizationFont.Roboto]: 'Roboto',
|
[CustomizationDefaultFont.Roboto]: 'Roboto',
|
||||||
[CustomizationFont.RobotoSlab]: 'Roboto Slab',
|
[CustomizationDefaultFont.RobotoSlab]: 'Roboto Slab',
|
||||||
[CustomizationFont.SourceSansPro]: 'Source Sans 3',
|
[CustomizationDefaultFont.SourceSansPro]: 'Source Sans 3',
|
||||||
[CustomizationFont.Ubuntu]: 'Ubuntu',
|
[CustomizationDefaultFont.Ubuntu]: 'Ubuntu',
|
||||||
[CustomizationFont.ABCFavorit]: 'Inter',
|
[CustomizationDefaultFont.ABCFavorit]: 'Inter',
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -49,18 +49,12 @@ export function getTestURL(urlRest: string): string {
|
|||||||
* Get an API token for a site by its URL.
|
* Get an API token for a site by its URL.
|
||||||
*/
|
*/
|
||||||
export async function getSiteAPIToken(url: string) {
|
export async function getSiteAPIToken(url: string) {
|
||||||
const api = new GitBookAPI();
|
const api = new GitBookAPI({
|
||||||
const { data } = await api.urls.getPublishedContentByUrl(
|
endpoint: 'https://api.gitbook.com/cache',
|
||||||
{
|
});
|
||||||
|
const { data } = await api.urls.getPublishedContentByUrl({
|
||||||
url,
|
url,
|
||||||
cache: true,
|
});
|
||||||
},
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'x-gitbook-force-cache': 'true',
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if ('redirect' in data) {
|
if ('redirect' in data) {
|
||||||
throw new Error(`Invalid site URL, it resulted in a redirect: ${data.redirect}`);
|
throw new Error(`Invalid site URL, it resulted in a redirect: ${data.redirect}`);
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"classnames": "^2.5.1",
|
"classnames": "^2.5.1",
|
||||||
"@gitbook/api": "0.96.1",
|
"@gitbook/api": "*",
|
||||||
"@gitbook/icons": "workspace:*"
|
"@gitbook/icons": "workspace:*"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
+1
-1
@@ -37,7 +37,7 @@
|
|||||||
},
|
},
|
||||||
// Check the package for type errors
|
// Check the package for type errors
|
||||||
"typecheck": {
|
"typecheck": {
|
||||||
"dependsOn": ["^typecheck", "build", "generate"]
|
"dependsOn": ["^typecheck", "build"]
|
||||||
},
|
},
|
||||||
// Lint the package for style errors
|
// Lint the package for style errors
|
||||||
"lint": {
|
"lint": {
|
||||||
|
|||||||
Reference in New Issue
Block a user