mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-10-28 07:57:02 +00:00
Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d74c78af6f | |||
| 6f8ecd6111 | |||
| fa3eb07617 | |||
| dc4268db64 | |||
| a0c06a72cc | |||
| 19c20c0be3 | |||
| fae34e56ac | |||
| ba0094a862 | |||
| b259009c9c | |||
| d88dd5ed8a | |||
| 57bb146075 | |||
| 4b67fe5317 |
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"gitbook": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Fix navigation between sections/variants when previewing a site in v2
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"gitbook-v2": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
fix ISR on preview env
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
'@gitbook/react-openapi': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Indent JSON python code sample
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"gitbook": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Make TOC height dynamic based on visible header and footer elements
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
"gitbook": patch
|
||||||
|
"gitbook-v2": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
cache fonts and static image used in OGImage in memory
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"gitbook-v2": patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Add `urlObject.hash` to `linker.toLinkForContent` to pass through URL fragment identifiers, used in search
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
},
|
},
|
||||||
"packages/colors": {
|
"packages/colors": {
|
||||||
"name": "@gitbook/colors",
|
"name": "@gitbook/colors",
|
||||||
"version": "0.3.2",
|
"version": "0.3.3",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"typescript": "^5.5.3",
|
"typescript": "^5.5.3",
|
||||||
},
|
},
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
},
|
},
|
||||||
"packages/gitbook": {
|
"packages/gitbook": {
|
||||||
"name": "gitbook",
|
"name": "gitbook",
|
||||||
"version": "0.11.1",
|
"version": "0.12.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "^0.115.0",
|
"@gitbook/api": "^0.115.0",
|
||||||
"@gitbook/cache-do": "workspace:*",
|
"@gitbook/cache-do": "workspace:*",
|
||||||
@@ -141,11 +141,11 @@
|
|||||||
},
|
},
|
||||||
"packages/gitbook-v2": {
|
"packages/gitbook-v2": {
|
||||||
"name": "gitbook-v2",
|
"name": "gitbook-v2",
|
||||||
"version": "0.2.5",
|
"version": "0.3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "^0.115.0",
|
"@gitbook/api": "^0.115.0",
|
||||||
"@gitbook/cache-tags": "workspace:*",
|
"@gitbook/cache-tags": "workspace:*",
|
||||||
"@opennextjs/cloudflare": "1.0.3",
|
"@opennextjs/cloudflare": "1.0.4",
|
||||||
"@sindresorhus/fnv1a": "^3.1.0",
|
"@sindresorhus/fnv1a": "^3.1.0",
|
||||||
"assert-never": "^1.2.1",
|
"assert-never": "^1.2.1",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
@@ -185,7 +185,7 @@
|
|||||||
},
|
},
|
||||||
"packages/openapi-parser": {
|
"packages/openapi-parser": {
|
||||||
"name": "@gitbook/openapi-parser",
|
"name": "@gitbook/openapi-parser",
|
||||||
"version": "2.1.3",
|
"version": "2.1.4",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@scalar/openapi-parser": "^0.10.10",
|
"@scalar/openapi-parser": "^0.10.10",
|
||||||
"@scalar/openapi-types": "^0.1.9",
|
"@scalar/openapi-types": "^0.1.9",
|
||||||
@@ -232,7 +232,7 @@
|
|||||||
},
|
},
|
||||||
"packages/react-openapi": {
|
"packages/react-openapi": {
|
||||||
"name": "@gitbook/react-openapi",
|
"name": "@gitbook/react-openapi",
|
||||||
"version": "1.2.1",
|
"version": "1.3.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/openapi-parser": "workspace:*",
|
"@gitbook/openapi-parser": "workspace:*",
|
||||||
"@scalar/api-client-react": "^1.2.19",
|
"@scalar/api-client-react": "^1.2.19",
|
||||||
@@ -791,9 +791,9 @@
|
|||||||
|
|
||||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||||
|
|
||||||
"@opennextjs/aws": ["@opennextjs/aws@3.6.1", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@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", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-RYU9K58vEUPXqc3pZO6kr9vBy1MmJZFQZLe0oXBskC005oGju/m4e3DCCP4eZ/Q/HdYQXCoqNXgSGi8VCAYgew=="],
|
"@opennextjs/aws": ["@opennextjs/aws@3.6.2", "", { "dependencies": { "@ast-grep/napi": "^0.35.0", "@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.25.4", "express": "5.0.1", "path-to-regexp": "^6.3.0", "urlpattern-polyfill": "^10.0.0", "yaml": "^2.7.0" }, "bin": { "open-next": "dist/index.js" } }, "sha512-26/3GSoj7mKN7XpQFikYM2Lrwal5jlMc4fJO//QAdd5bCSnBaWBAkzr7+VvXAFVIC6eBeDLdtlWiQuUQVEAPZQ=="],
|
||||||
|
|
||||||
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.3", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "^3.6.1", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.14.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-+SNhz2JmKkMlWzndTSYZeq0eseAPfEXUUYJA7H9MkgFe36i1no0OdiLuwu9e4uR3yuOzaNWB+skCYHsD6Im9nA=="],
|
"@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.4", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "^3.6.2", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.14.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-DVudGpOSJ91ruPiBJ0kuFmPhEQbXIXLTbvUjAx1OlbwFskG2gvdNIAmF3ZXV6z1VGDO7Q/u2W2ybMZLf7avlrA=="],
|
||||||
|
|
||||||
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
"@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="],
|
||||||
|
|
||||||
@@ -3649,7 +3649,7 @@
|
|||||||
|
|
||||||
"@node-minify/core/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
|
"@node-minify/core/mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild": ["esbuild@0.19.2", "", { "optionalDependencies": { "@esbuild/android-arm": "0.19.2", "@esbuild/android-arm64": "0.19.2", "@esbuild/android-x64": "0.19.2", "@esbuild/darwin-arm64": "0.19.2", "@esbuild/darwin-x64": "0.19.2", "@esbuild/freebsd-arm64": "0.19.2", "@esbuild/freebsd-x64": "0.19.2", "@esbuild/linux-arm": "0.19.2", "@esbuild/linux-arm64": "0.19.2", "@esbuild/linux-ia32": "0.19.2", "@esbuild/linux-loong64": "0.19.2", "@esbuild/linux-mips64el": "0.19.2", "@esbuild/linux-ppc64": "0.19.2", "@esbuild/linux-riscv64": "0.19.2", "@esbuild/linux-s390x": "0.19.2", "@esbuild/linux-x64": "0.19.2", "@esbuild/netbsd-x64": "0.19.2", "@esbuild/openbsd-x64": "0.19.2", "@esbuild/sunos-x64": "0.19.2", "@esbuild/win32-arm64": "0.19.2", "@esbuild/win32-ia32": "0.19.2", "@esbuild/win32-x64": "0.19.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg=="],
|
"@opennextjs/aws/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="],
|
||||||
|
|
||||||
"@radix-ui/react-collection/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
"@radix-ui/react-collection/@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw=="],
|
||||||
|
|
||||||
@@ -4721,49 +4721,55 @@
|
|||||||
|
|
||||||
"@node-minify/core/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
"@node-minify/core/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.19.2", "", { "os": "android", "cpu": "arm" }, "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q=="],
|
"@opennextjs/aws/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.19.2", "", { "os": "android", "cpu": "arm64" }, "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw=="],
|
"@opennextjs/aws/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.19.2", "", { "os": "android", "cpu": "x64" }, "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w=="],
|
"@opennextjs/aws/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.19.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA=="],
|
"@opennextjs/aws/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.19.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw=="],
|
"@opennextjs/aws/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.19.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ=="],
|
"@opennextjs/aws/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.19.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw=="],
|
"@opennextjs/aws/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.19.2", "", { "os": "linux", "cpu": "arm" }, "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg=="],
|
"@opennextjs/aws/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.19.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.19.2", "", { "os": "linux", "cpu": "ia32" }, "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.19.2", "", { "os": "linux", "cpu": "none" }, "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.19.2", "", { "os": "linux", "cpu": "none" }, "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.19.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.19.2", "", { "os": "linux", "cpu": "none" }, "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.19.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.19.2", "", { "os": "linux", "cpu": "x64" }, "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.19.2", "", { "os": "none", "cpu": "x64" }, "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ=="],
|
"@opennextjs/aws/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.19.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw=="],
|
"@opennextjs/aws/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.19.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw=="],
|
"@opennextjs/aws/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.19.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg=="],
|
"@opennextjs/aws/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.19.2", "", { "os": "win32", "cpu": "ia32" }, "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA=="],
|
"@opennextjs/aws/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="],
|
||||||
|
|
||||||
"@opennextjs/aws/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.19.2", "", { "os": "win32", "cpu": "x64" }, "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw=="],
|
"@opennextjs/aws/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="],
|
||||||
|
|
||||||
|
"@opennextjs/aws/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="],
|
||||||
|
|
||||||
|
"@opennextjs/aws/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="],
|
||||||
|
|
||||||
|
"@opennextjs/aws/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="],
|
||||||
|
|
||||||
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
|
"@radix-ui/react-dismissable-layer/@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g=="],
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { defineCloudflareConfig } from '@opennextjs/cloudflare';
|
import { defineCloudflareConfig } from '@opennextjs/cloudflare';
|
||||||
import doQueue from '@opennextjs/cloudflare/overrides/queue/do-queue';
|
|
||||||
import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache';
|
import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache';
|
||||||
import {
|
import {
|
||||||
softTagFilter,
|
softTagFilter,
|
||||||
@@ -20,7 +19,7 @@ export default defineCloudflareConfig({
|
|||||||
// We don't use `revalidatePath`, so we filter out soft tags
|
// We don't use `revalidatePath`, so we filter out soft tags
|
||||||
filterFn: softTagFilter,
|
filterFn: softTagFilter,
|
||||||
}),
|
}),
|
||||||
queue: doQueue,
|
queue: () => import('./openNext/queue').then((m) => m.default),
|
||||||
|
|
||||||
// Performance improvements as we don't use PPR
|
// Performance improvements as we don't use PPR
|
||||||
enableCacheInterception: true,
|
enableCacheInterception: true,
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import type { Queue } from '@opennextjs/aws/types/overrides.js';
|
||||||
|
import { getCloudflareContext } from '@opennextjs/cloudflare';
|
||||||
|
import doQueue from '@opennextjs/cloudflare/overrides/queue/do-queue';
|
||||||
|
import memoryQueue from '@opennextjs/cloudflare/overrides/queue/memory-queue';
|
||||||
|
|
||||||
|
interface Env {
|
||||||
|
IS_PREVIEW?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'GitbookISRQueue',
|
||||||
|
send: async (msg) => {
|
||||||
|
const { ctx, env } = getCloudflareContext();
|
||||||
|
const isPreview = (env as Env).IS_PREVIEW === 'true';
|
||||||
|
ctx.waitUntil(isPreview ? memoryQueue.send(msg) : doQueue.send(msg));
|
||||||
|
},
|
||||||
|
} satisfies Queue;
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@gitbook/api": "^0.115.0",
|
"@gitbook/api": "^0.115.0",
|
||||||
"@gitbook/cache-tags": "workspace:*",
|
"@gitbook/cache-tags": "workspace:*",
|
||||||
"@opennextjs/cloudflare": "1.0.3",
|
"@opennextjs/cloudflare": "1.0.4",
|
||||||
"@sindresorhus/fnv1a": "^3.1.0",
|
"@sindresorhus/fnv1a": "^3.1.0",
|
||||||
"assert-never": "^1.2.1",
|
"assert-never": "^1.2.1",
|
||||||
"jwt-decode": "^4.0.0",
|
"jwt-decode": "^4.0.0",
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
import { type NextRequest, NextResponse } from 'next/server';
|
import { type NextRequest, NextResponse } from 'next/server';
|
||||||
|
|
||||||
import { withVerifySignature } from '@v2/lib/routes';
|
import { withVerifySignature } from '@v2/lib/routes';
|
||||||
import { revalidateTag } from 'next/cache';
|
import { revalidatePath, revalidateTag } from 'next/cache';
|
||||||
|
|
||||||
interface JsonBody {
|
interface JsonBody {
|
||||||
tags: string[];
|
tags: string[];
|
||||||
|
paths?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Revalidate cached data based on tags.
|
* Revalidate cached data based on tags.
|
||||||
* The body should be a JSON with { tags: string[] }
|
* The body should be a JSON with { tags: string[], paths?: string[] }
|
||||||
*/
|
*/
|
||||||
export async function POST(req: NextRequest) {
|
export async function POST(req: NextRequest) {
|
||||||
return withVerifySignature<JsonBody>(req, async (body) => {
|
return withVerifySignature<JsonBody>(req, async (body) => {
|
||||||
@@ -22,12 +23,27 @@ export async function POST(req: NextRequest) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (body.paths && !Array.isArray(body.paths)) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: 'paths must be an array',
|
||||||
|
},
|
||||||
|
{ status: 400 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
body.tags.forEach((tag) => {
|
body.tags.forEach((tag) => {
|
||||||
// biome-ignore lint/suspicious/noConsole: we want to log here
|
// biome-ignore lint/suspicious/noConsole: we want to log here
|
||||||
console.log(`Revalidating tag: ${tag}`);
|
console.log(`Revalidating tag: ${tag}`);
|
||||||
revalidateTag(tag);
|
revalidateTag(tag);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
body.paths?.forEach((path) => {
|
||||||
|
// biome-ignore lint/suspicious/noConsole: we want to log here
|
||||||
|
console.log(`Revalidating path: ${path}`);
|
||||||
|
revalidatePath(path);
|
||||||
|
});
|
||||||
|
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ export function getBaseContext(input: {
|
|||||||
// Create link in the same format for links to other sites/sections.
|
// Create link in the same format for links to other sites/sections.
|
||||||
linker.toLinkForContent = (rawURL: string) => {
|
linker.toLinkForContent = (rawURL: string) => {
|
||||||
const urlObject = new URL(rawURL);
|
const urlObject = new URL(rawURL);
|
||||||
return `/url/${urlObject.host}${urlObject.pathname}${urlObject.search}`;
|
return `/url/${urlObject.host}${urlObject.pathname}${urlObject.search}${urlObject.hash}`;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,4 @@ export * from './pages';
|
|||||||
export * from './urls';
|
export * from './urls';
|
||||||
export * from './errors';
|
export * from './errors';
|
||||||
export * from './lookup';
|
export * from './lookup';
|
||||||
export * from './proxy';
|
|
||||||
export * from './visitor';
|
export * from './visitor';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { withLeadingSlash, withTrailingSlash } from '@/lib/paths';
|
import { withLeadingSlash, withTrailingSlash } from '@/lib/paths';
|
||||||
import type { PublishedSiteContent } from '@gitbook/api';
|
import type { PublishedSiteContent } from '@gitbook/api';
|
||||||
import { getProxyRequestIdentifier, isProxyRequest } from './proxy';
|
import { getProxyRequestIdentifier, isProxyRequest } from '@v2/lib/proxy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the appropriate base path for the visitor authentication cookie.
|
* Get the appropriate base path for the visitor authentication cookie.
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { describe, expect, it } from 'bun:test';
|
||||||
|
import { getImageResizingContextId } from './getImageResizingContextId';
|
||||||
|
|
||||||
|
describe('getImageResizingContextId', () => {
|
||||||
|
it('should return proxy identifier for proxy requests', () => {
|
||||||
|
const proxyRequestURL = new URL('https://proxy.gitbook.site/sites/site_foo/hello/world');
|
||||||
|
expect(getImageResizingContextId(proxyRequestURL)).toBe('sites/site_foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return preview identifier for preview requests', () => {
|
||||||
|
const previewRequestURL = new URL('https://preview/site_foo/hello/world');
|
||||||
|
expect(getImageResizingContextId(previewRequestURL)).toBe('site_foo');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return host for regular requests', () => {
|
||||||
|
const regularRequestURL = new URL('https://example.com/docs/foo/hello/world');
|
||||||
|
expect(getImageResizingContextId(regularRequestURL)).toBe('example.com');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { getProxyRequestIdentifier, isProxyRequest } from '../data';
|
import { getPreviewRequestIdentifier, isPreviewRequest } from '@v2/lib/preview';
|
||||||
|
import { getProxyRequestIdentifier, isProxyRequest } from '@v2/lib/proxy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the site identifier to use for image resizing for an incoming request.
|
* Get the site identifier to use for image resizing for an incoming request.
|
||||||
@@ -8,6 +9,9 @@ export function getImageResizingContextId(url: URL): string {
|
|||||||
if (isProxyRequest(url)) {
|
if (isProxyRequest(url)) {
|
||||||
return getProxyRequestIdentifier(url);
|
return getProxyRequestIdentifier(url);
|
||||||
}
|
}
|
||||||
|
if (isPreviewRequest(url)) {
|
||||||
|
return getPreviewRequestIdentifier(url);
|
||||||
|
}
|
||||||
|
|
||||||
return url.host;
|
return url.host;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import { describe, expect, it } from 'bun:test';
|
||||||
|
import { getPreviewRequestIdentifier, isPreviewRequest } from './preview';
|
||||||
|
|
||||||
|
describe('isPreviewRequest', () => {
|
||||||
|
it('should return true for preview requests', () => {
|
||||||
|
const previewRequestURL = new URL('https://preview/site_foo/hello/world');
|
||||||
|
expect(isPreviewRequest(previewRequestURL)).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return false for non-preview requests', () => {
|
||||||
|
const nonPreviewRequestURL = new URL('https://example.com/docs/foo/hello/world');
|
||||||
|
expect(isPreviewRequest(nonPreviewRequestURL)).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getPreviewRequestIdentifier', () => {
|
||||||
|
it('should return the correct identifier for preview requests', () => {
|
||||||
|
const previewRequestURL = new URL('https://preview/site_foo/hello/world');
|
||||||
|
expect(getPreviewRequestIdentifier(previewRequestURL)).toBe('site_foo');
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Check if the request to the site is a preview request.
|
||||||
|
*/
|
||||||
|
export function isPreviewRequest(requestURL: URL): boolean {
|
||||||
|
return requestURL.host === 'preview';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPreviewRequestIdentifier(requestURL: URL): string {
|
||||||
|
// For preview requests, we extract the site ID from the pathname
|
||||||
|
// e.g. https://preview/site_id/...
|
||||||
|
const pathname = requestURL.pathname.slice(1).split('/');
|
||||||
|
return pathname[0];
|
||||||
|
}
|
||||||
@@ -15,10 +15,14 @@
|
|||||||
"enabled": true
|
"enabled": true
|
||||||
},
|
},
|
||||||
"vars": {
|
"vars": {
|
||||||
"NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE": "true"
|
"NEXT_CACHE_DO_QUEUE_DISABLE_SQLITE": "true",
|
||||||
|
"IS_PREVIEW": "false"
|
||||||
},
|
},
|
||||||
"env": {
|
"env": {
|
||||||
"preview": {
|
"preview": {
|
||||||
|
"vars": {
|
||||||
|
"IS_PREVIEW": "true"
|
||||||
|
},
|
||||||
"r2_buckets": [
|
"r2_buckets": [
|
||||||
{
|
{
|
||||||
"binding": "NEXT_INC_CACHE_R2_BUCKET",
|
"binding": "NEXT_INC_CACHE_R2_BUCKET",
|
||||||
@@ -91,9 +95,12 @@
|
|||||||
},
|
},
|
||||||
"production": {
|
"production": {
|
||||||
"vars": {
|
"vars": {
|
||||||
// This is a bit misleading, but it means that we can have 100 concurrent revalidations
|
// This is a bit misleading, but it means that we can have 500 concurrent revalidations
|
||||||
// This means that we'll have up to 20 durable objects instance running at the same time
|
// This means that we'll have up to 100 durable objects instance running at the same time
|
||||||
"MAX_REVALIDATE_CONCURRENCY": "20"
|
"MAX_REVALIDATE_CONCURRENCY": "100",
|
||||||
|
// Temporary variable to find the issue once deployed
|
||||||
|
// TODO: remove this once the issue is fixed
|
||||||
|
"DEBUG_CLOUDFLARE": "true"
|
||||||
},
|
},
|
||||||
"routes": [
|
"routes": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ export function AnnouncementBanner(props: {
|
|||||||
const style = BANNER_STYLES[announcement.style];
|
const style = BANNER_STYLES[announcement.style];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="announcement-banner theme-bold:bg-header-background pt-4 pb-2">
|
<div id="announcement-banner" className="theme-bold:bg-header-background pt-4 pb-2">
|
||||||
<div className="scroll-nojump">
|
<div className="scroll-nojump">
|
||||||
<div className={tcls('relative', CONTAINER_STYLE)}>
|
<div className={tcls('relative', CONTAINER_STYLE)}>
|
||||||
<Tag
|
<Tag
|
||||||
|
|||||||
@@ -115,7 +115,8 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
|||||||
return <Tag className={tcls(['w-full', verticalAlignment])}>{''}</Tag>;
|
return <Tag className={tcls(['w-full', verticalAlignment])}>{''}</Tag>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const horizontalAlignment = `[&_*]:${getColumnAlignment(definition)} ${getColumnAlignment(definition)}`;
|
const horizontalAlignment = getColumnAlignment(definition);
|
||||||
|
const childrenHorizontalAlignment = `[&_*]:${horizontalAlignment}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Blocks
|
<Blocks
|
||||||
@@ -131,6 +132,7 @@ export async function RecordColumnValue<Tag extends React.ElementType = 'div'>(
|
|||||||
'leading-normal',
|
'leading-normal',
|
||||||
verticalAlignment,
|
verticalAlignment,
|
||||||
horizontalAlignment,
|
horizontalAlignment,
|
||||||
|
childrenHorizontalAlignment,
|
||||||
]}
|
]}
|
||||||
context={context}
|
context={context}
|
||||||
blockStyle={['w-full', 'max-w-[unset]']}
|
blockStyle={['w-full', 'max-w-[unset]']}
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ export function Footer(props: { context: GitBookSiteContext }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<footer
|
<footer
|
||||||
|
id="site-footer"
|
||||||
className={tcls(
|
className={tcls(
|
||||||
'border-tint-subtle border-t',
|
'border-tint-subtle border-t',
|
||||||
// If the footer only contains a mode toggle, we only show it on smaller screens
|
// If the footer only contains a mode toggle, we only show it on smaller screens
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import type { SiteSpace } from '@gitbook/api';
|
|||||||
|
|
||||||
import { tcls } from '@/lib/tailwind';
|
import { tcls } from '@/lib/tailwind';
|
||||||
|
|
||||||
|
import { joinPath } from '@/lib/paths';
|
||||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||||
import { DropdownChevron, DropdownMenu } from './DropdownMenu';
|
import { DropdownChevron, DropdownMenu } from './DropdownMenu';
|
||||||
import { SpacesDropdownMenuItem } from './SpacesDropdownMenuItem';
|
import { SpacesDropdownMenuItem } from './SpacesDropdownMenuItem';
|
||||||
@@ -74,7 +75,7 @@ export function SpacesDropdown(props: {
|
|||||||
title: otherSiteSpace.title,
|
title: otherSiteSpace.title,
|
||||||
url: otherSiteSpace.urls.published
|
url: otherSiteSpace.urls.published
|
||||||
? linker.toLinkForContent(otherSiteSpace.urls.published)
|
? linker.toLinkForContent(otherSiteSpace.urls.published)
|
||||||
: otherSiteSpace.space.urls.app,
|
: getFallbackSiteSpaceURL(otherSiteSpace, context),
|
||||||
}}
|
}}
|
||||||
active={otherSiteSpace.id === siteSpace.id}
|
active={otherSiteSpace.id === siteSpace.id}
|
||||||
/>
|
/>
|
||||||
@@ -82,3 +83,14 @@ export function SpacesDropdown(props: {
|
|||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When the site is not published yet, `urls.published` is not available.
|
||||||
|
* To ensure navigation works in preview, we compute a relative URL from the siteSpace path.
|
||||||
|
*/
|
||||||
|
function getFallbackSiteSpaceURL(siteSpace: SiteSpace, context: GitBookSiteContext) {
|
||||||
|
const { linker, sections } = context;
|
||||||
|
return linker.toPathInSite(
|
||||||
|
sections?.current ? joinPath(sections.current.path, siteSpace.path) : siteSpace.path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export async function CustomizationRootLayout(props: {
|
|||||||
lang={customization.internationalization.locale}
|
lang={customization.internationalization.locale}
|
||||||
className={tcls(
|
className={tcls(
|
||||||
customization.header.preset === CustomizationHeaderPreset.None
|
customization.header.preset === CustomizationHeaderPreset.None
|
||||||
? 'site-header-none'
|
? null
|
||||||
: 'scroll-pt-[76px]', // Take the sticky header in consideration for the scrolling
|
: 'scroll-pt-[76px]', // Take the sticky header in consideration for the scrolling
|
||||||
customization.styling.corners === CustomizationCorners.Straight
|
customization.styling.corners === CustomizationCorners.Straight
|
||||||
? ' straight-corners'
|
? ' straight-corners'
|
||||||
|
|||||||
@@ -161,6 +161,6 @@ html.dark {
|
|||||||
color-scheme: dark light;
|
color-scheme: dark light;
|
||||||
}
|
}
|
||||||
|
|
||||||
html.announcement-hidden .announcement-banner {
|
html.announcement-hidden #announcement-banner {
|
||||||
@apply hidden;
|
@apply hidden;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ function encodeSection(context: GitBookSiteContext, section: SiteSection) {
|
|||||||
description: section.description,
|
description: section.description,
|
||||||
icon: section.icon,
|
icon: section.icon,
|
||||||
object: section.object,
|
object: section.object,
|
||||||
url: section.urls.published ? linker.toLinkForContent(section.urls.published) : '',
|
url: section.urls.published
|
||||||
|
? linker.toLinkForContent(section.urls.published)
|
||||||
|
: linker.toPathInSite(section.path),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { tcls } from '@/lib/tailwind';
|
|||||||
|
|
||||||
import { PagesList } from './PagesList';
|
import { PagesList } from './PagesList';
|
||||||
import { TOCScrollContainer } from './TOCScroller';
|
import { TOCScrollContainer } from './TOCScroller';
|
||||||
|
import { TableOfContentsScript } from './TableOfContentsScript';
|
||||||
import { Trademark } from './Trademark';
|
import { Trademark } from './Trademark';
|
||||||
|
|
||||||
export function TableOfContents(props: {
|
export function TableOfContents(props: {
|
||||||
@@ -17,97 +18,107 @@ export function TableOfContents(props: {
|
|||||||
const { space, customization, pages } = context;
|
const { space, customization, pages } = context;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside // Sidebar container, responsible for setting the right dimensions and position for the sidebar.
|
<>
|
||||||
data-testid="table-of-contents"
|
<aside // Sidebar container, responsible for setting the right dimensions and position for the sidebar.
|
||||||
className={tcls(
|
data-testid="table-of-contents"
|
||||||
'group',
|
id="table-of-contents"
|
||||||
'text-sm',
|
|
||||||
|
|
||||||
'grow-0',
|
|
||||||
'shrink-0',
|
|
||||||
'basis-full',
|
|
||||||
'lg:basis-72',
|
|
||||||
'page-no-toc:lg:basis-56',
|
|
||||||
|
|
||||||
'relative',
|
|
||||||
'z-[1]',
|
|
||||||
'lg:sticky',
|
|
||||||
// Without header
|
|
||||||
'lg:top-0',
|
|
||||||
'lg:h-screen',
|
|
||||||
|
|
||||||
// With header
|
|
||||||
'site-header:lg:top-16',
|
|
||||||
'site-header:lg:h-[calc(100vh_-_4rem)]',
|
|
||||||
|
|
||||||
// With header and sections
|
|
||||||
'site-header-sections:lg:top-[6.75rem]',
|
|
||||||
'site-header-sections:lg:h-[calc(100vh_-_6.75rem)]',
|
|
||||||
|
|
||||||
'pt-6',
|
|
||||||
'pb-4',
|
|
||||||
'sidebar-filled:lg:pr-6',
|
|
||||||
'page-no-toc:lg:pr-0',
|
|
||||||
|
|
||||||
'hidden',
|
|
||||||
'navigation-open:!flex',
|
|
||||||
'lg:flex',
|
|
||||||
'page-no-toc:lg:hidden',
|
|
||||||
'page-no-toc:xl:flex',
|
|
||||||
'site-header-none:page-no-toc:lg:flex',
|
|
||||||
'flex-col',
|
|
||||||
'gap-4',
|
|
||||||
|
|
||||||
'navigation-open:border-b',
|
|
||||||
'border-tint-subtle'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{header && header}
|
|
||||||
<div // The actual sidebar, either shown with a filled bg or transparent.
|
|
||||||
className={tcls(
|
className={tcls(
|
||||||
'lg:-ms-5',
|
'group',
|
||||||
'relative flex flex-grow flex-col overflow-hidden border-tint-subtle',
|
'text-sm',
|
||||||
|
|
||||||
'sidebar-filled:bg-tint-subtle',
|
'grow-0',
|
||||||
'theme-muted:bg-tint-subtle',
|
'shrink-0',
|
||||||
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
|
'basis-full',
|
||||||
'[html.sidebar-filled.theme-muted_&]:bg-tint-base',
|
'lg:basis-72',
|
||||||
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-base',
|
'page-no-toc:lg:basis-56',
|
||||||
'[html.sidebar-filled.theme-gradient_&]:border',
|
|
||||||
'page-no-toc:!bg-transparent',
|
|
||||||
|
|
||||||
'sidebar-filled:rounded-xl',
|
'relative',
|
||||||
'straight-corners:rounded-none'
|
'z-[1]',
|
||||||
|
'lg:sticky',
|
||||||
|
|
||||||
|
// Server-side static positioning
|
||||||
|
'lg:top-0',
|
||||||
|
'lg:h-screen',
|
||||||
|
'announcement:lg:h-[calc(100vh-4.25rem)]',
|
||||||
|
|
||||||
|
'site-header:lg:top-16',
|
||||||
|
'site-header:lg:h-[calc(100vh-4rem)]',
|
||||||
|
'announcement:site-header:lg:h-[calc(100vh-4rem-4.25rem)]',
|
||||||
|
|
||||||
|
'site-header-sections:lg:top-[6.75rem]',
|
||||||
|
'site-header-sections:lg:h-[calc(100vh-6.75rem)]',
|
||||||
|
'announcement:site-header-sections:lg:h-[calc(100vh-6.75rem-4.25rem)]',
|
||||||
|
|
||||||
|
// Client-side dynamic positioning (CSS vars applied by script)
|
||||||
|
'[html[style*="--toc-top-offset"]_&]:lg:!top-[var(--toc-top-offset)]',
|
||||||
|
'[html[style*="--toc-height"]_&]:lg:!h-[var(--toc-height)]',
|
||||||
|
|
||||||
|
'pt-6',
|
||||||
|
'pb-4',
|
||||||
|
'sidebar-filled:lg:pr-6',
|
||||||
|
'page-no-toc:lg:pr-0',
|
||||||
|
|
||||||
|
'hidden',
|
||||||
|
'navigation-open:!flex',
|
||||||
|
'lg:flex',
|
||||||
|
'page-no-toc:lg:hidden',
|
||||||
|
'page-no-toc:xl:flex',
|
||||||
|
'site-header-none:page-no-toc:lg:flex',
|
||||||
|
'flex-col',
|
||||||
|
'gap-4',
|
||||||
|
|
||||||
|
'navigation-open:border-b',
|
||||||
|
'border-tint-subtle'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{innerHeader && <div className="px-5 *:my-4">{innerHeader}</div>}
|
{header && header}
|
||||||
<TOCScrollContainer // The scrollview inside the sidebar
|
<div // The actual sidebar, either shown with a filled bg or transparent.
|
||||||
className={tcls(
|
className={tcls(
|
||||||
'flex flex-grow flex-col p-2',
|
'lg:-ms-5',
|
||||||
customization.trademark.enabled && 'lg:pb-20',
|
'relative flex flex-grow flex-col overflow-hidden border-tint-subtle',
|
||||||
'lg:gutter-stable overflow-y-auto',
|
|
||||||
'[&::-webkit-scrollbar]:bg-transparent',
|
'sidebar-filled:bg-tint-subtle',
|
||||||
'[&::-webkit-scrollbar-thumb]:bg-transparent',
|
'theme-muted:bg-tint-subtle',
|
||||||
'group-hover:[&::-webkit-scrollbar]:bg-tint-subtle',
|
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-subtle',
|
||||||
'group-hover:[&::-webkit-scrollbar-thumb]:bg-tint-7',
|
'[html.sidebar-filled.theme-muted_&]:bg-tint-base',
|
||||||
'group-hover:[&::-webkit-scrollbar-thumb:hover]:bg-tint-8'
|
'[html.sidebar-filled.theme-bold.tint_&]:bg-tint-base',
|
||||||
|
'[html.sidebar-filled.theme-gradient_&]:border',
|
||||||
|
'page-no-toc:!bg-transparent',
|
||||||
|
|
||||||
|
'sidebar-filled:rounded-xl',
|
||||||
|
'straight-corners:rounded-none'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<PagesList
|
{innerHeader && <div className="px-5 *:my-4">{innerHeader}</div>}
|
||||||
rootPages={pages}
|
<TOCScrollContainer // The scrollview inside the sidebar
|
||||||
pages={pages}
|
className={tcls(
|
||||||
context={context}
|
'flex flex-grow flex-col p-2',
|
||||||
style="page-no-toc:hidden border-tint-subtle sidebar-list-line:border-l"
|
customization.trademark.enabled && 'lg:pb-20',
|
||||||
/>
|
'lg:gutter-stable overflow-y-auto',
|
||||||
{customization.trademark.enabled ? (
|
'[&::-webkit-scrollbar]:bg-transparent',
|
||||||
<Trademark
|
'[&::-webkit-scrollbar-thumb]:bg-transparent',
|
||||||
space={space}
|
'group-hover:[&::-webkit-scrollbar]:bg-tint-subtle',
|
||||||
customization={customization}
|
'group-hover:[&::-webkit-scrollbar-thumb]:bg-tint-7',
|
||||||
placement={SiteInsightsTrademarkPlacement.Sidebar}
|
'group-hover:[&::-webkit-scrollbar-thumb:hover]:bg-tint-8'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<PagesList
|
||||||
|
rootPages={pages}
|
||||||
|
pages={pages}
|
||||||
|
context={context}
|
||||||
|
style="page-no-toc:hidden border-tint-subtle sidebar-list-line:border-l"
|
||||||
/>
|
/>
|
||||||
) : null}
|
{customization.trademark.enabled ? (
|
||||||
</TOCScrollContainer>
|
<Trademark
|
||||||
</div>
|
space={space}
|
||||||
</aside>
|
customization={customization}
|
||||||
|
placement={SiteInsightsTrademarkPlacement.Sidebar}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</TOCScrollContainer>
|
||||||
|
</div>
|
||||||
|
</aside>
|
||||||
|
<TableOfContentsScript />
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adjusts TableOfContents height based on visible elements
|
||||||
|
*/
|
||||||
|
export function TableOfContentsScript() {
|
||||||
|
useEffect(() => {
|
||||||
|
const root = document.documentElement;
|
||||||
|
|
||||||
|
// Calculate and set TOC dimensions
|
||||||
|
const updateTocLayout = () => {
|
||||||
|
// Get key elements
|
||||||
|
const header = document.getElementById('site-header');
|
||||||
|
const banner = document.getElementById('announcement-banner');
|
||||||
|
const footer = document.getElementById('site-footer');
|
||||||
|
|
||||||
|
// Set sticky top position based on header
|
||||||
|
const headerHeight = header?.offsetHeight ?? 0;
|
||||||
|
root.style.setProperty('--toc-top-offset', `${headerHeight}px`);
|
||||||
|
|
||||||
|
// Start with full viewport height minus header
|
||||||
|
let height = window.innerHeight - headerHeight;
|
||||||
|
|
||||||
|
// Subtract visible banner (if any)
|
||||||
|
if (banner && window.getComputedStyle(banner).display !== 'none') {
|
||||||
|
const bannerRect = banner.getBoundingClientRect();
|
||||||
|
if (bannerRect.height > 0 && bannerRect.bottom > 0) {
|
||||||
|
height -= Math.min(bannerRect.height, bannerRect.bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Subtract visible footer (if any)
|
||||||
|
if (footer) {
|
||||||
|
const footerRect = footer.getBoundingClientRect();
|
||||||
|
if (footerRect.top < window.innerHeight) {
|
||||||
|
height -= Math.min(footerRect.height, window.innerHeight - footerRect.top);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update height
|
||||||
|
root.style.setProperty('--toc-height', `${height}px`);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initial update
|
||||||
|
updateTocLayout();
|
||||||
|
|
||||||
|
// Let the browser handle scroll throttling naturally
|
||||||
|
window.addEventListener('scroll', updateTocLayout, { passive: true });
|
||||||
|
window.addEventListener('resize', updateTocLayout, { passive: true });
|
||||||
|
|
||||||
|
// Use MutationObserver for DOM changes
|
||||||
|
const observer = new MutationObserver(() => {
|
||||||
|
requestAnimationFrame(updateTocLayout);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Only observe what matters
|
||||||
|
observer.observe(document.documentElement, {
|
||||||
|
subtree: true,
|
||||||
|
attributes: true,
|
||||||
|
attributeFilter: ['style', 'class'],
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.disconnect();
|
||||||
|
window.removeEventListener('scroll', updateTocLayout);
|
||||||
|
window.removeEventListener('resize', updateTocLayout);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
@@ -1 +1,4 @@
|
|||||||
export * from './TableOfContents';
|
export { TableOfContents } from './TableOfContents';
|
||||||
|
export { PagesList } from './PagesList';
|
||||||
|
export { TOCScrollContainer } from './TOCScroller';
|
||||||
|
export { Trademark } from './Trademark';
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { getAssetURL } from '@/lib/assets';
|
|||||||
import { filterOutNullable } from '@/lib/typescript';
|
import { filterOutNullable } from '@/lib/typescript';
|
||||||
import { getCacheTag } from '@gitbook/cache-tags';
|
import { getCacheTag } from '@gitbook/cache-tags';
|
||||||
import type { GitBookSiteContext } from '@v2/lib/context';
|
import type { GitBookSiteContext } from '@v2/lib/context';
|
||||||
import { getCloudflareContext } from '@v2/lib/data/cloudflare';
|
|
||||||
import { getResizedImageURL } from '@v2/lib/images';
|
import { getResizedImageURL } from '@v2/lib/images';
|
||||||
|
|
||||||
const googleFontsMap: { [fontName in CustomizationDefaultFont]: string } = {
|
const googleFontsMap: { [fontName in CustomizationDefaultFont]: string } = {
|
||||||
@@ -73,8 +72,12 @@ export async function serveOGImage(baseContext: GitBookSiteContext, params: Page
|
|||||||
|
|
||||||
const fonts = (
|
const fonts = (
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
loadGoogleFont({ fontFamily, text: regularText, weight: 400 }),
|
getWithCache(`google-font:${fontFamily}:400`, () =>
|
||||||
loadGoogleFont({ fontFamily, text: boldText, weight: 700 }),
|
loadGoogleFont({ fontFamily, text: regularText, weight: 400 })
|
||||||
|
),
|
||||||
|
getWithCache(`google-font:${fontFamily}:700`, () =>
|
||||||
|
loadGoogleFont({ fontFamily, text: boldText, weight: 700 })
|
||||||
|
),
|
||||||
])
|
])
|
||||||
).filter(filterOutNullable);
|
).filter(filterOutNullable);
|
||||||
|
|
||||||
@@ -312,20 +315,14 @@ async function loadCustomFont(input: { url: string; weight: 400 | 700 }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch a resource from the function itself.
|
* Temporary function to log some data on Cloudflare.
|
||||||
* To avoid error with worker to worker requests in the same zone, we use the `WORKER_SELF_REFERENCE` binding.
|
* TODO: remove this when we found the issue
|
||||||
*/
|
*/
|
||||||
async function fetchSelf(url: string) {
|
function logOnCloudflareOnly(message: string) {
|
||||||
const cloudflare = getCloudflareContext();
|
if (process.env.DEBUG_CLOUDFLARE === 'true') {
|
||||||
if (cloudflare?.env.WORKER_SELF_REFERENCE) {
|
// biome-ignore lint/suspicious/noConsole: <explanation>
|
||||||
return await cloudflare.env.WORKER_SELF_REFERENCE.fetch(
|
console.log(message);
|
||||||
// `getAssetURL` can return a relative URL, so we need to make it absolute
|
|
||||||
// the URL doesn't matter, as we're using the worker-self-reference binding
|
|
||||||
new URL(url, 'https://worker-self-reference/')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return await fetch(url);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -334,6 +331,9 @@ async function fetchSelf(url: string) {
|
|||||||
async function readImage(response: Response) {
|
async function readImage(response: Response) {
|
||||||
const contentType = response.headers.get('content-type');
|
const contentType = response.headers.get('content-type');
|
||||||
if (!contentType || !contentType.startsWith('image/')) {
|
if (!contentType || !contentType.startsWith('image/')) {
|
||||||
|
logOnCloudflareOnly(`Invalid content type: ${contentType},
|
||||||
|
status: ${response.status}
|
||||||
|
rayId: ${response.headers.get('cf-ray')}`);
|
||||||
throw new Error(`Invalid content type: ${contentType}`);
|
throw new Error(`Invalid content type: ${contentType}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,27 +342,34 @@ async function readImage(response: Response) {
|
|||||||
return `data:${contentType};base64,${base64}`;
|
return `data:${contentType};base64,${base64}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const staticImagesCache = new Map<string, string>();
|
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
||||||
|
const staticCache = new Map<string, any>();
|
||||||
|
|
||||||
|
// Do we need to limit the in-memory cache size? I think given the usage, we should be fine.
|
||||||
|
async function getWithCache<T>(key: string, fn: () => Promise<T>) {
|
||||||
|
const cached = staticCache.get(key) as T;
|
||||||
|
if (cached) {
|
||||||
|
return Promise.resolve(cached);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await fn();
|
||||||
|
staticCache.set(key, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read a static image and cache it in memory.
|
* Read a static image and cache it in memory.
|
||||||
*/
|
*/
|
||||||
async function readStaticImage(url: string) {
|
async function readStaticImage(url: string) {
|
||||||
const cached = staticImagesCache.get(url);
|
logOnCloudflareOnly(`Reading static image: ${url}, cache size: ${staticCache.size}`);
|
||||||
if (cached) {
|
return getWithCache(`static-image:${url}`, () => readSelfImage(url));
|
||||||
return cached;
|
|
||||||
}
|
|
||||||
|
|
||||||
const image = await readSelfImage(url);
|
|
||||||
staticImagesCache.set(url, image);
|
|
||||||
return image;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read an image from GitBook itself.
|
* Read an image from GitBook itself.
|
||||||
*/
|
*/
|
||||||
async function readSelfImage(url: string) {
|
async function readSelfImage(url: string) {
|
||||||
const response = await fetchSelf(url);
|
const response = await fetch(url);
|
||||||
const image = await readImage(response);
|
const image = await readImage(response);
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -458,12 +458,16 @@ const config: Config = {
|
|||||||
/**
|
/**
|
||||||
* Variant when a header is displayed.
|
* Variant when a header is displayed.
|
||||||
*/
|
*/
|
||||||
addVariant('site-header-none', 'html.site-header-none &');
|
addVariant('site-header-none', 'body:not(:has(#site-header:not(.mobile-only))) &');
|
||||||
addVariant('site-header', 'body:has(#site-header:not(.mobile-only)) &');
|
addVariant('site-header', 'body:has(#site-header:not(.mobile-only)) &');
|
||||||
addVariant('site-header-sections', [
|
addVariant('site-header-sections', [
|
||||||
'body:has(#site-header:not(.mobile-only) #sections) &',
|
'body:has(#site-header:not(.mobile-only) #sections) &',
|
||||||
'body:has(.page-no-toc):has(#site-header:not(.mobile-only) #variants) &',
|
'body:has(.page-no-toc):has(#site-header:not(.mobile-only) #variants) &',
|
||||||
]);
|
]);
|
||||||
|
addVariant(
|
||||||
|
'announcement',
|
||||||
|
'html:not(.announcement-hidden):has(#announcement-banner) &'
|
||||||
|
);
|
||||||
|
|
||||||
const customisationVariants = {
|
const customisationVariants = {
|
||||||
// Sidebar styles
|
// Sidebar styles
|
||||||
|
|||||||
@@ -400,7 +400,7 @@ describe('python code sample generator', () => {
|
|||||||
const output = generator?.generate(input);
|
const output = generator?.generate(input);
|
||||||
|
|
||||||
expect(output).toBe(
|
expect(output).toBe(
|
||||||
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/x-www-form-urlencoded"},\n data={"key":"value"}\n)\n\ndata = response.json()'
|
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/x-www-form-urlencoded"},\n data={\n "key": "value"\n }\n)\n\ndata = response.json()'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -422,7 +422,7 @@ describe('python code sample generator', () => {
|
|||||||
const output = generator?.generate(input);
|
const output = generator?.generate(input);
|
||||||
|
|
||||||
expect(output).toBe(
|
expect(output).toBe(
|
||||||
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({"key":"value","truethy":True,"falsey":False,"nullish":None})\n)\n\ndata = response.json()'
|
'import requests\n\nresponse = requests.get(\n "https://example.com/path",\n headers={"Content-Type":"application/json"},\n data=json.dumps({\n "key": "value",\n "truethy": True,\n "falsey": False,\n "nullish": None\n })\n)\n\ndata = response.json()'
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -356,18 +356,22 @@ const BodyGenerators = {
|
|||||||
// Convert JSON to XML if needed
|
// Convert JSON to XML if needed
|
||||||
body = JSON.stringify(convertBodyToXML(body));
|
body = JSON.stringify(convertBodyToXML(body));
|
||||||
} else {
|
} else {
|
||||||
body = stringifyOpenAPI(body, (_key, value) => {
|
body = stringifyOpenAPI(
|
||||||
switch (value) {
|
body,
|
||||||
case true:
|
(_key, value) => {
|
||||||
return '$$__TRUE__$$';
|
switch (value) {
|
||||||
case false:
|
case true:
|
||||||
return '$$__FALSE__$$';
|
return '$$__TRUE__$$';
|
||||||
case null:
|
case false:
|
||||||
return '$$__NULL__$$';
|
return '$$__FALSE__$$';
|
||||||
default:
|
case null:
|
||||||
return value;
|
return '$$__NULL__$$';
|
||||||
}
|
default:
|
||||||
})
|
return value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
2
|
||||||
|
)
|
||||||
.replaceAll('"$$__TRUE__$$"', 'True')
|
.replaceAll('"$$__TRUE__$$"', 'True')
|
||||||
.replaceAll('"$$__FALSE__$$"', 'False')
|
.replaceAll('"$$__FALSE__$$"', 'False')
|
||||||
.replaceAll('"$$__NULL__$$"', 'None');
|
.replaceAll('"$$__NULL__$$"', 'None');
|
||||||
|
|||||||
Reference in New Issue
Block a user