From 44a54280ec857082021722152b0440b6cc2a8146 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 08:35:13 +0200 Subject: [PATCH 01/29] Upgrade opennext to upcoming version --- bun.lock | 26 +++++++++++++------------- package.json | 2 +- packages/gitbook-v2/package.json | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/bun.lock b/bun.lock index d720c44b84..38ae35c920 100644 --- a/bun.lock +++ b/bun.lock @@ -156,7 +156,7 @@ "warn-once": "^0.1.1", }, "devDependencies": { - "@opennextjs/cloudflare": "^1.0.0-beta.3", + "@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@a8815be", "@types/rison": "^0.0.9", "gitbook": "*", "postcss": "^8", @@ -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=="], - "@opennextjs/aws": ["@opennextjs/aws@3.5.7", "", { "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-YjyHJrkIHI7YwQRCp8GjDOudu86oOc1RiwxvBBpPHrplsS18H4ZmkzGggAKhK6B4myGsJQ/q9kNP2TraoZiNzg=="], + "@opennextjs/aws": ["@opennextjs/aws@3.6.0", "", { "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-fLhPgg9ftV8GKNxuoeRJevKa+LIGMMzY9LIGni+5FO9GyhVf0V93j0WTf2x/g9yk5I64hukMWmCqTszitaZ5yg=="], - "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.0-beta.3", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.5.7", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^3.114.3 || ^4.7.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-qKBXQZhUeQ+iGvfJeF7PO30g59LHnPOlRVZd77zxwn6Uc9C+c0LSwo8N28XRIWyQPkY007rKk9pSIxOrP4MHtQ=="], + "@opennextjs/cloudflare": ["@opennextjs/cloudflare@https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@a8815be", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.6.0", "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" } }], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], @@ -4077,7 +4077,7 @@ "gaxios/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "gitbook-v2/next": ["next@15.4.0-canary.7", "", { "dependencies": { "@next/env": "15.4.0-canary.7", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.7", "@next/swc-darwin-x64": "15.4.0-canary.7", "@next/swc-linux-arm64-gnu": "15.4.0-canary.7", "@next/swc-linux-arm64-musl": "15.4.0-canary.7", "@next/swc-linux-x64-gnu": "15.4.0-canary.7", "@next/swc-linux-x64-musl": "15.4.0-canary.7", "@next/swc-win32-arm64-msvc": "15.4.0-canary.7", "@next/swc-win32-x64-msvc": "15.4.0-canary.7", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-ZYjT0iu+4osz8XIlr31MuoXaNQKRU75UcwEgNBt93gftoh6tzV2Mebz6sOGeVReYuYUvYlLJJksMBTNcFcPbSA=="], + "gitbook-v2/next": ["next@15.4.0-canary.17", "", { "dependencies": { "@next/env": "15.4.0-canary.17", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.17", "@next/swc-darwin-x64": "15.4.0-canary.17", "@next/swc-linux-arm64-gnu": "15.4.0-canary.17", "@next/swc-linux-arm64-musl": "15.4.0-canary.17", "@next/swc-linux-x64-gnu": "15.4.0-canary.17", "@next/swc-linux-x64-musl": "15.4.0-canary.17", "@next/swc-win32-arm64-msvc": "15.4.0-canary.17", "@next/swc-win32-x64-msvc": "15.4.0-canary.17", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-AH14xNNvz+6S33AB3TR+VHkS+cnhrzkNeh8TnYpSbtouQqhJtm0lTYjnQTGqa3cHGKMgLI1h13MU724HiJGbcg=="], "global-dirs/ini": ["ini@1.3.7", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="], @@ -4969,23 +4969,23 @@ "gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], - "gitbook-v2/next/@next/env": ["@next/env@15.4.0-canary.7", "", {}, "sha512-q8S7f2lQti3Y3gcAPzE8Pj8y0EwiWHVyyilMzoLbDPXGVfxlQhXLRiFdy2cDkKN4DyjGZWDeehEtw4huvJAa3Q=="], + "gitbook-v2/next/@next/env": ["@next/env@15.4.0-canary.17", "", {}, "sha512-k0dqCJhBYqPyRr/i+M9OYEtn9nJuZjJf9y6jVOkwl+xaEr4K6UmIBu6vVfoWitDwPLRj4NgKmHgkH75dPRxAQg=="], - "gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.7", "", { "os": "darwin", "cpu": "arm64" }, "sha512-+TMxUu5CAWNe+UFRc47BZAXQxCRqZfVbGyCldddiog4MorvL7kBxSd1qlmrwI73fRRKtXkHIH1TaeItyxzC9rQ=="], + "gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CX2ZV8DsLjbX6eYf0OhLMfjZnFykwzAcG6gnz9fJdU+l20YvYj5BlWAefSTLncQ6KWq/FLb8XU00PL8ThaGHPg=="], - "gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.7", "", { "os": "darwin", "cpu": "x64" }, "sha512-veXp8lg/X/7O+pG9BDQ3OizFz3B40v29jsvEWj+ULY/W8Z6+dCSd5XPP2M8fG/gKKKA0D6L0CnnM2Mj0RRSUJw=="], + "gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-J2cdOuAAxpVbjk6QRHPtq1hbDXuNIgCGB/G4HvKot71VIKXg/Fb1IaxwFiBYNl0zsRgH5CPO+AO9pUUGwpVPyQ=="], - "gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-KxNGfW7BO0Z5B9rJyl9p7YVjNrxAhu06mH6h1PSdouZG7YMYpdRCconVXeuBI0PEu6g3ywNrOVxZUk1V6G5u0Q=="], + "gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-DKUs71Gi/leUH9RrMRamVfxXfOKdNGov7CH1KufeCyKIl9PNQQhP5GMgUk/19nvNmyFV7PjxIXRxElgBzRPtyQ=="], - "gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.7", "", { "os": "linux", "cpu": "arm64" }, "sha512-THgXgmP/cC4DsNwvC6uqB90CebB7Ep1KyZajQL3fYKT5V4SWr46yngKLyoyJVeAYWJH908MrWddf7Ya/Zq7cyg=="], + "gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-k4tjv818/f2DLmCZOkqTEsnqDpIUeKFb1gH9WnexjfAJLD2RXmb+hde8PDB64/Syf2RdytREdPZBnDs2okxrbQ=="], - "gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-kpLB3Jj7fProynQYj2ahFyZlJs0xwm71VzCVrNRu6u7qJGXn6dK5h7+hro8y/y1iqjXWgCLSdxWSHahhWK8XdQ=="], + "gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.17", "", { "os": "linux", "cpu": "x64" }, "sha512-5vLVdcMKG2iMPTUu0E9aAvVBbEwKJeCA9CGb1hsQuEuTA3N3XntRHvZKj32POTE4jYJeb0DnxEZHli7SiyOBYw=="], - "gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.7", "", { "os": "linux", "cpu": "x64" }, "sha512-rnGAKvl4cWPVV9D+SybWOGijm0VmKXyqQ+IN0A6WDgdlYZAZP0ZnJv/rq7DSvuOh19AXS8UpQc88SelXV/3j3Q=="], + "gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.17", "", { "os": "linux", "cpu": "x64" }, "sha512-N5YAd9chWQdLJayfkgZP+d/daTAWKa2jm0SPNhjZiez88fkoZlBU+GPLsozA7L3RdaqLGqDPrvavylt1Iw0L9Q=="], - "gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.7", "", { "os": "win32", "cpu": "arm64" }, "sha512-/PRbn//EuR3UGiquk050gqvjxLliEgGBy1Cx9KkpAT7szaHOBj1mDDQmxMTEhRex4i3YfKGJXWn5mLMCveya6Q=="], + "gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-URAe7qnVC8tarSBx/V/WDRP3xzkb8tr3m9gEjIYbBBVF64xlzrPshcPe5HIssGjL/5u3NRs9VxgsE9xZ7CcxKA=="], - "gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.7", "", { "os": "win32", "cpu": "x64" }, "sha512-7a92XL+DlrbWyycCpQjjQMHOrsA0p+VvS7iA2dyi89Xsq0qtOPzFH0Gb56fsjh6M6BQGFhboOSzjmpjlkMTilQ=="], + "gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.17", "", { "os": "win32", "cpu": "x64" }, "sha512-kEFB4sX2/LM/OpFl08TrV7SIntFJKOh4RfvdUFQ++OTSHDLZz+G24FtRPHx6b6ULWWdNndkY2WfBMH9qy9RfBQ=="], "gitbook-v2/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], diff --git a/package.json b/package.json index 00f323e2f4..8dfd14ef8b 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "turbo": "^2.5.0", "vercel": "^39.3.0" }, - "packageManager": "bun@1.2.8", + "packageManager": "bun@1.2.11", "overrides": { "@codemirror/state": "6.4.1", "@gitbook/api": "0.113.0", diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json index 50cf96a0d7..a92ed9badf 100644 --- a/packages/gitbook-v2/package.json +++ b/packages/gitbook-v2/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "gitbook": "*", - "@opennextjs/cloudflare": "^1.0.0-beta.3", + "@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@a8815be", "@types/rison": "^0.0.9", "tailwindcss": "^3.4.0", "postcss": "^8" From df9f5a89d424a89bdc74edc424b1ac1e5152dd6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 08:40:17 +0200 Subject: [PATCH 02/29] Enable global_fetch_strictly_public --- packages/gitbook-v2/wrangler.jsonc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/gitbook-v2/wrangler.jsonc b/packages/gitbook-v2/wrangler.jsonc index e316e2a242..7fbecf3bc6 100644 --- a/packages/gitbook-v2/wrangler.jsonc +++ b/packages/gitbook-v2/wrangler.jsonc @@ -2,7 +2,11 @@ "main": ".open-next/worker.js", "name": "gitbook-open-v2", "compatibility_date": "2025-04-14", - "compatibility_flags": ["nodejs_compat", "allow_importable_env"], + "compatibility_flags": [ + "nodejs_compat", + "allow_importable_env", + "global_fetch_strictly_public" + ], "assets": { "directory": ".open-next/assets", "binding": "ASSETS" From 1e7de51c04681e2b9fbeb3c37b1ce6a8b1811e61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 08:43:44 +0200 Subject: [PATCH 03/29] Enable withFilter --- packages/gitbook-v2/open-next.config.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/gitbook-v2/open-next.config.ts b/packages/gitbook-v2/open-next.config.ts index 8f4d389da6..2ce66f794b 100644 --- a/packages/gitbook-v2/open-next.config.ts +++ b/packages/gitbook-v2/open-next.config.ts @@ -3,16 +3,24 @@ import r2IncrementalCache from '@opennextjs/cloudflare/overrides/incremental-cac import { withRegionalCache } from '@opennextjs/cloudflare/overrides/incremental-cache/regional-cache'; import doQueue from '@opennextjs/cloudflare/overrides/queue/do-queue'; import doShardedTagCache from '@opennextjs/cloudflare/overrides/tag-cache/do-sharded-tag-cache'; +import { + softTagFilter, + withFilter, +} from '@opennextjs/cloudflare/overrides/tag-cache/tag-cache-filter'; export default defineCloudflareConfig({ incrementalCache: withRegionalCache(r2IncrementalCache, { mode: 'long-lived' }), - tagCache: doShardedTagCache({ - baseShardSize: 12, - regionalCache: true, - shardReplication: { - numberOfSoftReplicas: 2, - numberOfHardReplicas: 1, - }, + tagCache: withFilter({ + tagCache: doShardedTagCache({ + baseShardSize: 12, + regionalCache: true, + shardReplication: { + numberOfSoftReplicas: 2, + numberOfHardReplicas: 1, + }, + }), + // We don't use `revalidatePath`, so we filter out soft tags + filterFn: softTagFilter, }), queue: doQueue, }); From 8e3895b737de097e24183b21c57c52623b7c37c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 09:30:48 +0200 Subject: [PATCH 04/29] Use use cache on cloudflare --- packages/gitbook-v2/src/lib/data/api.ts | 43 +++++++++++------------ packages/gitbook/src/lib/openapi/fetch.ts | 16 ++------- 2 files changed, 22 insertions(+), 37 deletions(-) diff --git a/packages/gitbook-v2/src/lib/data/api.ts b/packages/gitbook-v2/src/lib/data/api.ts index 20e495656e..f9cecdf0d8 100644 --- a/packages/gitbook-v2/src/lib/data/api.ts +++ b/packages/gitbook-v2/src/lib/data/api.ts @@ -7,12 +7,7 @@ import { type RenderIntegrationUI, } from '@gitbook/api'; import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags'; -import { - GITBOOK_API_TOKEN, - GITBOOK_API_URL, - GITBOOK_RUNTIME, - 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_cache } from 'next/cache'; import { getCloudflareContext, getCloudflareRequestGlobal } from './cloudflare'; @@ -20,6 +15,8 @@ import { DataFetcherError, wrapDataFetcherError } from './errors'; import { withCacheKey, withoutConcurrentExecution } from './memoize'; import type { GitBookDataFetcher } from './types'; +const withUseCache = true; + interface DataFetcherInput { /** * API token. @@ -221,7 +218,7 @@ const getUserById = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async (cacheKey, input: DataFetcherInput, params: { userId: string }) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getUserByIdUseCache(input, params); } @@ -273,7 +270,7 @@ const getSpace = withCacheKey( input: DataFetcherInput, params: { spaceId: string; shareKey: string | undefined } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getSpaceUseCache(input, params); } @@ -345,7 +342,7 @@ const getChangeRequest = withCacheKey( input: DataFetcherInput, params: { spaceId: string; changeRequestId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getChangeRequestUseCache(input, params); } @@ -422,7 +419,7 @@ const getRevision = withCacheKey( input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getRevisionUseCache(input, params); } @@ -479,7 +476,7 @@ const getRevisionPages = withCacheKey( input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getRevisionPagesUseCache(input, params); } @@ -540,7 +537,7 @@ const getRevisionFile = withCacheKey( input: DataFetcherInput, params: { spaceId: string; revisionId: string; fileId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getRevisionFileUseCache(input, params); } @@ -603,7 +600,7 @@ const getRevisionPageMarkdown = withCacheKey( input: DataFetcherInput, params: { spaceId: string; revisionId: string; pageId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getRevisionPageMarkdownUseCache(input, params); } @@ -673,7 +670,7 @@ const getRevisionPageByPath = withCacheKey( input: DataFetcherInput, params: { spaceId: string; revisionId: string; path: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getRevisionPageByPathUseCache(input, params); } @@ -737,7 +734,7 @@ const getDocument = withCacheKey( input: DataFetcherInput, params: { spaceId: string; documentId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getDocumentUseCache(input, params); } @@ -797,7 +794,7 @@ const getComputedDocument = withCacheKey( seed: string; } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getComputedDocumentUseCache(input, params); } @@ -886,7 +883,7 @@ const getReusableContent = withCacheKey( input: DataFetcherInput, params: { spaceId: string; revisionId: string; reusableContentId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getReusableContentUseCache(input, params); } @@ -948,7 +945,7 @@ const getLatestOpenAPISpecVersionContent = withCacheKey( input: DataFetcherInput, params: { organizationId: string; slug: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getLatestOpenAPISpecVersionContentUseCache(input, params); } @@ -1025,7 +1022,7 @@ const getPublishedContentSite = withCacheKey( input: DataFetcherInput, params: { organizationId: string; siteId: string; siteShareKey: string | undefined } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getPublishedContentSiteUseCache(input, params); } @@ -1108,7 +1105,7 @@ const getSiteRedirectBySource = withCacheKey( source: string; } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getSiteRedirectBySourceUseCache(input, params); } @@ -1193,7 +1190,7 @@ const getEmbedByUrl = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async (cacheKey, input: DataFetcherInput, params: { spaceId: string; url: string }) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return getEmbedByUrlUseCache(input, params); } @@ -1259,7 +1256,7 @@ const searchSiteContent = withCacheKey( input: DataFetcherInput, params: Parameters[0] ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return searchSiteContentUseCache(input, params); } @@ -1330,7 +1327,7 @@ const renderIntegrationUi = withCacheKey( input: DataFetcherInput, params: { integrationName: string; request: RenderIntegrationUI } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { + if (withUseCache) { return renderIntegrationUiUseCache(input, params); } diff --git a/packages/gitbook/src/lib/openapi/fetch.ts b/packages/gitbook/src/lib/openapi/fetch.ts index d1f8c03cef..b496bbe59f 100644 --- a/packages/gitbook/src/lib/openapi/fetch.ts +++ b/packages/gitbook/src/lib/openapi/fetch.ts @@ -1,5 +1,4 @@ import { parseOpenAPI } from '@gitbook/openapi-parser'; -import { unstable_cache } from 'next/cache'; import { type CacheFunctionOptions, cache, noCacheFetchOptions } from '@/lib/cache'; import type { @@ -10,7 +9,6 @@ import type { } from '@/lib/openapi/types'; import { getCloudflareRequestGlobal } from '@v2/lib/data/cloudflare'; import { withCacheKey, withoutConcurrentExecution } from '@v2/lib/data/memoize'; -import { GITBOOK_RUNTIME } from '@v2/lib/env'; import { assert } from 'ts-essentials'; import { resolveContentRef } from '../references'; import { isV2 } from '../v2'; @@ -71,18 +69,8 @@ const fetchFilesystemV1 = cache({ }); const fetchFilesystemV2 = withCacheKey( - withoutConcurrentExecution(getCloudflareRequestGlobal, async (cacheKey, url: string) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return fetchFilesystemUseCache(url); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache(async () => fetchFilesystemUncached(url), [cacheKey], { - revalidate: 60 * 60 * 24, - }); - - const response = await uncached(); - return response; + withoutConcurrentExecution(getCloudflareRequestGlobal, async (_cacheKey, url: string) => { + return fetchFilesystemUseCache(url); }) ); From 461c8505aa4dd0db2dc1801eee88a687d13824b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 09:53:09 +0200 Subject: [PATCH 05/29] Remove all use of unstable_cache --- packages/gitbook-v2/src/lib/data/api.ts | 1290 ++++++----------------- 1 file changed, 319 insertions(+), 971 deletions(-) diff --git a/packages/gitbook-v2/src/lib/data/api.ts b/packages/gitbook-v2/src/lib/data/api.ts index f9cecdf0d8..b71e4cb186 100644 --- a/packages/gitbook-v2/src/lib/data/api.ts +++ b/packages/gitbook-v2/src/lib/data/api.ts @@ -9,14 +9,11 @@ import { import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags'; import { GITBOOK_API_TOKEN, GITBOOK_API_URL, GITBOOK_USER_AGENT } from '@v2/lib/env'; import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache'; -import { unstable_cache } from 'next/cache'; import { getCloudflareContext, getCloudflareRequestGlobal } from './cloudflare'; import { DataFetcherError, wrapDataFetcherError } from './errors'; import { withCacheKey, withoutConcurrentExecution } from './memoize'; import type { GitBookDataFetcher } from './types'; -const withUseCache = true; - interface DataFetcherInput { /** * API token. @@ -24,18 +21,6 @@ interface DataFetcherInput { apiToken: string | null; } -/** - * Revalidation profile for the cache. - * Based on https://nextjs.org/docs/app/api-reference/functions/cacheLife#default-cache-profiles - */ -enum RevalidationProfile { - minutes = 60, - hours = 60 * 60, - days = 60 * 60 * 24, - weeks = 60 * 60 * 24 * 7, - max = 60 * 60 * 24 * 30, -} - /** * Create a data fetcher using an API token. * The data are being cached by Next.js built-in cache. @@ -203,589 +188,272 @@ export function createDataFetcher( }; } -/* - * For the following functions, we: - * - Wrap them with `withCacheKey` to compute a cache key from the function arguments ONCE (to be performant) - * - Pass the cache key to `unstable_cache` to ensure the cache is not tied to closures - * - Call the uncached function in a `withoutConcurrentExecution` wrapper to prevent concurrent executions - * - * Important: - * - Only the function inside the `unstable_cache` is wrapped in `withoutConcurrentExecution` as Next.js needs to call - * the return of `unstable_cache` to identify the tags. - */ - const getUserById = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async (cacheKey, input: DataFetcherInput, params: { userId: string }) => { - if (withUseCache) { - return getUserByIdUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getUserByIdUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [], - } - ); - - return uncached(); + async (_, input: DataFetcherInput, params: { userId: string }) => { + 'use cache'; + return trace(`getUserById(${params.userId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.users.getUserById(params.userId); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); + }); } ) ); -const getUserByIdUseCache = async (input: DataFetcherInput, params: { userId: string }) => { - 'use cache'; - return getUserByIdUncached(input, params, true); -}; - -const getUserByIdUncached = async ( - input: DataFetcherInput, - params: { userId: string }, - withUseCache = false -) => { - return trace(`getUserById.uncached(${params.userId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.users.getUserById(params.userId); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - }); -}; - const getSpace = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; shareKey: string | undefined } ) => { - if (withUseCache) { - return getSpaceUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getSpaceUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'space', - space: params.spaceId, - }), - ], - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'space', + space: params.spaceId, + }) ); - return uncached(); + return trace(`getSpace(${params.spaceId}, ${params.shareKey})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getSpaceById(params.spaceId, { + shareKey: params.shareKey, + }); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); + }); } ) ); -const getSpaceUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; shareKey: string | undefined } -) => { - 'use cache'; - return getSpaceUncached(input, params, true); -}; - -const getSpaceUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; shareKey: string | undefined }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'space', - space: params.spaceId, - }) - ); - } - - return trace(`getSpace.uncached(${params.spaceId}, ${params.shareKey})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getSpaceById(params.spaceId, { - shareKey: params.shareKey, - }); - - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - }); -}; - const getChangeRequest = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; changeRequestId: string } ) => { - if (withUseCache) { - return getChangeRequestUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'change-request', + space: params.spaceId, + changeRequest: params.changeRequestId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getChangeRequest(${params.spaceId}, ${params.changeRequestId})`, async () => { - return getChangeRequestUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.minutes * 5, - tags: [ - getCacheTag({ - tag: 'change-request', - space: params.spaceId, - changeRequest: params.changeRequestId, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getChangeRequestById( + params.spaceId, + params.changeRequestId + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('minutes'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getChangeRequestUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; changeRequestId: string } -) => { - 'use cache'; - return getChangeRequestUncached(input, params, true); -}; - -const getChangeRequestUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; changeRequestId: string }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'change-request', - space: params.spaceId, - changeRequest: params.changeRequestId, - }) - ); - } - - return trace( - `getChangeRequest.uncached(${params.spaceId}, ${params.changeRequestId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getChangeRequestById( - params.spaceId, - params.changeRequestId - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('minutes'); - } - return res.data; - }); - } - ); -}; - const getRevision = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - if (withUseCache) { - return getRevisionUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getRevisionUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], - } - ); - - return uncached(); + 'use cache'; + return trace(`getRevision(${params.spaceId}, ${params.revisionId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getRevisionById( + params.spaceId, + params.revisionId, + { + metadata: params.metadata, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); + }); } ) ); -const getRevisionUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean } -) => { - 'use cache'; - return getRevisionUncached(input, params, true); -}; - -const getRevisionUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean }, - withUseCache = false -) => { - return trace(`getRevision.uncached(${params.spaceId}, ${params.revisionId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getRevisionById(params.spaceId, params.revisionId, { - metadata: params.metadata, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - }); -}; - const getRevisionPages = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - if (withUseCache) { - return getRevisionPagesUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getRevisionPagesUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], - } - ); - - return uncached(); + 'use cache'; + return trace(`getRevisionPages(${params.spaceId}, ${params.revisionId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.listPagesInRevisionById( + params.spaceId, + params.revisionId, + { + metadata: params.metadata, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data.pages; + }); + }); } ) ); -const getRevisionPagesUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean } -) => { - 'use cache'; - return getRevisionPagesUncached(input, params, true); -}; - -const getRevisionPagesUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean }, - withUseCache = false -) => { - return trace(`getRevisionPages.uncached(${params.spaceId}, ${params.revisionId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.listPagesInRevisionById( - params.spaceId, - params.revisionId, - { - metadata: params.metadata, - } - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data.pages; - }); - }); -}; - const getRevisionFile = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; fileId: string } ) => { - if (withUseCache) { - return getRevisionFileUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getRevisionFile(${params.spaceId}, ${params.revisionId}, ${params.fileId})`, async () => { - return getRevisionFileUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getFileInRevisionById( + params.spaceId, + params.revisionId, + params.fileId, + {} + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getRevisionFileUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; fileId: string } -) => { - 'use cache'; - return getRevisionFileUncached(input, params, true); -}; - -const getRevisionFileUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; fileId: string }, - withUseCache = false -) => { - return trace( - `getRevisionFile.uncached(${params.spaceId}, ${params.revisionId}, ${params.fileId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getFileInRevisionById( - params.spaceId, - params.revisionId, - params.fileId, - {} - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getRevisionPageMarkdown = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; pageId: string } ) => { - if (withUseCache) { - return getRevisionPageMarkdownUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getRevisionPageMarkdown(${params.spaceId}, ${params.revisionId}, ${params.pageId})`, async () => { - return getRevisionPageMarkdownUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getPageInRevisionById( + params.spaceId, + params.revisionId, + params.pageId, + { + format: 'markdown', + } + ); + + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + + if (!('markdown' in res.data)) { + throw new DataFetcherError('Page is not a document', 404); + } + return res.data.markdown; + }); } ); - - return uncached(); } ) ); -const getRevisionPageMarkdownUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; pageId: string } -) => { - 'use cache'; - return getRevisionPageMarkdownUncached(input, params, true); -}; - -const getRevisionPageMarkdownUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; pageId: string }, - withUseCache = false -) => { - return trace( - `getRevisionPageMarkdown.uncached(${params.spaceId}, ${params.revisionId}, ${params.pageId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getPageInRevisionById( - params.spaceId, - params.revisionId, - params.pageId, - { - format: 'markdown', - } - ); - - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - - if (!('markdown' in res.data)) { - throw new DataFetcherError('Page is not a document', 404); - } - return res.data.markdown; - }); - } - ); -}; - const getRevisionPageByPath = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; path: string } ) => { - if (withUseCache) { - return getRevisionPageByPathUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getRevisionPageByPath(${params.spaceId}, ${params.revisionId}, ${params.path})`, async () => { - return getRevisionPageByPathUncached(input, params); - }, - [cacheKey, 'v2'], - { - revalidate: RevalidationProfile.max, - tags: [], + const encodedPath = encodeURIComponent(params.path); + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getPageInRevisionByPath( + params.spaceId, + params.revisionId, + encodedPath, + {} + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getRevisionPageByPathUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; path: string } -) => { - 'use cache'; - return getRevisionPageByPathUncached(input, params, true); -}; - -const getRevisionPageByPathUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; path: string }, - withUseCache = false -) => { - return trace( - `getRevisionPageByPath.uncached(${params.spaceId}, ${params.revisionId}, ${params.path})`, - async () => { - const encodedPath = encodeURIComponent(params.path); - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getPageInRevisionByPath( - params.spaceId, - params.revisionId, - encodedPath, - {} - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getDocument = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async ( - cacheKey, - input: DataFetcherInput, - params: { spaceId: string; documentId: string } - ) => { - if (withUseCache) { - return getDocumentUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getDocumentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], - } - ); - - return uncached(); + async (_, input: DataFetcherInput, params: { spaceId: string; documentId: string }) => { + 'use cache'; + return trace(`getDocument(${params.spaceId}, ${params.documentId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getDocumentById( + params.spaceId, + params.documentId, + {} + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); + }); } ) ); -const getDocumentUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; documentId: string } -) => { - 'use cache'; - return getDocumentUncached(input, params, true); -}; - -const getDocumentUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; documentId: string }, - withUseCache = false -) => { - return trace(`getDocument.uncached(${params.spaceId}, ${params.documentId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getDocumentById(params.spaceId, params.documentId, {}); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - }); -}; - const getComputedDocument = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; @@ -794,309 +462,140 @@ const getComputedDocument = withCacheKey( seed: string; } ) => { - if (withUseCache) { - return getComputedDocumentUseCache(input, params); - } + 'use cache'; + cacheTag( + ...getComputedContentSourceCacheTags( + { + spaceId: params.spaceId, + organizationId: params.organizationId, + }, + params.source + ) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getComputedDocument(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`, async () => { - return getComputedDocumentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: getComputedContentSourceCacheTags( - { - spaceId: params.spaceId, - organizationId: params.organizationId, - }, - params.source - ), + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getComputedDocument(params.spaceId, { + source: params.source, + seed: params.seed, + }); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getComputedDocumentUseCache = async ( - input: DataFetcherInput, - params: { - spaceId: string; - organizationId: string; - source: ComputedContentSource; - seed: string; - } -) => { - 'use cache'; - return getComputedDocumentUncached(input, params, true); -}; - -const getComputedDocumentUncached = async ( - input: DataFetcherInput, - params: { - spaceId: string; - organizationId: string; - source: ComputedContentSource; - seed: string; - }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - ...getComputedContentSourceCacheTags( - { - spaceId: params.spaceId, - organizationId: params.organizationId, - }, - params.source - ) - ); - } - - return trace( - `getComputedDocument.uncached(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getComputedDocument(params.spaceId, { - source: params.source, - seed: params.seed, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getReusableContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; reusableContentId: string } ) => { - if (withUseCache) { - return getReusableContentUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getReusableContent(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`, async () => { - return getReusableContentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getReusableContentInRevisionById( + params.spaceId, + params.revisionId, + params.reusableContentId + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getReusableContentUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; reusableContentId: string } -) => { - 'use cache'; - return getReusableContentUncached(input, params, true); -}; - -const getReusableContentUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; reusableContentId: string }, - withUseCache = false -) => { - return trace( - `getReusableContent.uncached(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getReusableContentInRevisionById( - params.spaceId, - params.revisionId, - params.reusableContentId - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getLatestOpenAPISpecVersionContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async ( - cacheKey, - input: DataFetcherInput, - params: { organizationId: string; slug: string } - ) => { - if (withUseCache) { - return getLatestOpenAPISpecVersionContentUseCache(input, params); - } + async (_, input: DataFetcherInput, params: { organizationId: string; slug: string }) => { + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'openapi', + organization: params.organizationId, + openAPISpec: params.slug, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getLatestOpenAPISpecVersionContent(${params.organizationId}, ${params.slug})`, async () => { - return getLatestOpenAPISpecVersionContentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [ - getCacheTag({ - tag: 'openapi', - organization: params.organizationId, - openAPISpec: params.slug, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getLatestOpenApiSpecVersionContent( + params.organizationId, + params.slug + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getLatestOpenAPISpecVersionContentUseCache = async ( - input: DataFetcherInput, - params: { organizationId: string; slug: string } -) => { - 'use cache'; - return getLatestOpenAPISpecVersionContentUncached(input, params, true); -}; - -const getLatestOpenAPISpecVersionContentUncached = async ( - input: DataFetcherInput, - params: { organizationId: string; slug: string }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'openapi', - organization: params.organizationId, - openAPISpec: params.slug, - }) - ); - } - - return trace( - `getLatestOpenAPISpecVersionContent.uncached(${params.organizationId}, ${params.slug})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getLatestOpenApiSpecVersionContent( - params.organizationId, - params.slug - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getPublishedContentSite = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { organizationId: string; siteId: string; siteShareKey: string | undefined } ) => { - if (withUseCache) { - return getPublishedContentSiteUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getPublishedContentSite(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`, async () => { - return getPublishedContentSiteUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'site', - site: params.siteId, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getPublishedContentSite( + params.organizationId, + params.siteId, + { + shareKey: params.siteShareKey, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getPublishedContentSiteUseCache = async ( - input: DataFetcherInput, - params: { organizationId: string; siteId: string; siteShareKey: string | undefined } -) => { - 'use cache'; - return getPublishedContentSiteUncached(input, params, true); -}; - -const getPublishedContentSiteUncached = async ( - input: DataFetcherInput, - params: { organizationId: string; siteId: string; siteShareKey: string | undefined }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); - } - - return trace( - `getPublishedContentSite.uncached(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getPublishedContentSite( - params.organizationId, - params.siteId, - { - shareKey: params.siteShareKey, - } - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - } - ); -}; - const getSiteRedirectBySource = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { organizationId: string; @@ -1105,283 +604,132 @@ const getSiteRedirectBySource = withCacheKey( source: string; } ) => { - if (withUseCache) { - return getSiteRedirectBySourceUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getSiteRedirectBySource(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`, async () => { - return getSiteRedirectBySourceUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'site', - site: params.siteId, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getSiteRedirectBySource( + params.organizationId, + params.siteId, + { + shareKey: params.siteShareKey, + source: params.source, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getSiteRedirectBySourceUseCache = async ( - input: DataFetcherInput, - params: { - organizationId: string; - siteId: string; - siteShareKey: string | undefined; - source: string; - } -) => { - 'use cache'; - return getSiteRedirectBySourceUncached(input, params, true); -}; - -const getSiteRedirectBySourceUncached = async ( - input: DataFetcherInput, - params: { - organizationId: string; - siteId: string; - siteShareKey: string | undefined; - source: string; - }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); - } - - return trace( - `getSiteRedirectBySource.uncached(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getSiteRedirectBySource( - params.organizationId, - params.siteId, - { - shareKey: params.siteShareKey, - source: params.source, - } - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - } - ); -}; - const getEmbedByUrl = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async (cacheKey, input: DataFetcherInput, params: { spaceId: string; url: string }) => { - if (withUseCache) { - return getEmbedByUrlUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getEmbedByUrlUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.weeks, - tags: [], - } + async (_, input: DataFetcherInput, params: { spaceId: string; url: string }) => { + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'space', + space: params.spaceId, + }) ); - return uncached(); + return trace(`getEmbedByUrl(${params.spaceId}, ${params.url})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getEmbedByUrlInSpace(params.spaceId, { + url: params.url, + }); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('weeks'); + return res.data; + }); + }); } ) ); -const getEmbedByUrlUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; url: string } -) => { - 'use cache'; - return getEmbedByUrlUncached(input, params, true); -}; - -const getEmbedByUrlUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; url: string }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'space', - space: params.spaceId, - }) - ); - } - - return trace(`getEmbedByUrl.uncached(${params.spaceId}, ${params.url})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getEmbedByUrlInSpace(params.spaceId, { - url: params.url, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('weeks'); - } - return res.data; - }); - }); -}; - const searchSiteContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: Parameters[0] ) => { - if (withUseCache) { - return searchSiteContentUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `searchSiteContent(${params.organizationId}, ${params.siteId}, ${params.query})`, async () => { - return searchSiteContentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.hours, - tags: [], + return wrapDataFetcherError(async () => { + const { organizationId, siteId, query, scope } = params; + const api = apiClient(input); + const res = await api.orgs.searchSiteContent(organizationId, siteId, { + query, + ...scope, + }); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('hours'); + return res.data.items; + }); } ); - - return uncached(); } ) ); -const searchSiteContentUseCache = async ( - input: DataFetcherInput, - params: Parameters[0] -) => { - 'use cache'; - return searchSiteContentUncached(input, params, true); -}; - -const searchSiteContentUncached = async ( - input: DataFetcherInput, - params: Parameters[0], - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); - } - - return trace( - `searchSiteContent.uncached(${params.organizationId}, ${params.siteId}, ${params.query})`, - async () => { - return wrapDataFetcherError(async () => { - const { organizationId, siteId, query, scope } = params; - const api = apiClient(input); - const res = await api.orgs.searchSiteContent(organizationId, siteId, { - query, - ...scope, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('hours'); - } - return res.data.items; - }); - } - ); -}; - const renderIntegrationUi = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { integrationName: string; request: RenderIntegrationUI } ) => { - if (withUseCache) { - return renderIntegrationUiUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return renderIntegrationUiUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'integration', - integration: params.integrationName, - }), - ], - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'integration', + integration: params.integrationName, + }) ); - return uncached(); + return trace(`renderIntegrationUi(${params.integrationName})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.integrations.renderIntegrationUiWithPost( + params.integrationName, + params.request + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); + }); } ) ); -const renderIntegrationUiUseCache = async ( - input: DataFetcherInput, - params: { integrationName: string; request: RenderIntegrationUI } -) => { - 'use cache'; - return renderIntegrationUiUncached(input, params, true); -}; - -const renderIntegrationUiUncached = ( - input: DataFetcherInput, - params: { integrationName: string; request: RenderIntegrationUI }, - withUseCache = false -) => { - return trace(`renderIntegrationUi.uncached(${params.integrationName})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.integrations.renderIntegrationUiWithPost( - params.integrationName, - params.request - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - }); -}; - async function* streamAIResponse( input: DataFetcherInput, params: Parameters[0] From 7a7741b67394bd836e4da3cf2a3eb551657ccc19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 10:09:43 +0200 Subject: [PATCH 06/29] Log --- packages/gitbook-v2/src/lib/data/memoize.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/gitbook-v2/src/lib/data/memoize.ts b/packages/gitbook-v2/src/lib/data/memoize.ts index ff1ff5e841..442a4b76ea 100644 --- a/packages/gitbook-v2/src/lib/data/memoize.ts +++ b/packages/gitbook-v2/src/lib/data/memoize.ts @@ -10,6 +10,7 @@ export function withoutConcurrentExecution( return (key: string, ...args: ArgsType) => { const globalContext = getGlobalContext() ?? globalThis; + console.log('globalContext', globalContext); /** * Cache storage that is scoped to the current request when executed in Cloudflare Workers, @@ -21,6 +22,7 @@ export function withoutConcurrentExecution( const concurrent = promiseCache.get(key); if (concurrent) { + console.log('returning concurrent value for', key); return concurrent; } From 4b32059f511b13d7cb575d041db7ecbc92bcb883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 10:57:33 +0200 Subject: [PATCH 07/29] Disable withoutConcurrentExecution for now --- packages/gitbook-v2/src/lib/data/memoize.ts | 57 +++++++++++---------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/packages/gitbook-v2/src/lib/data/memoize.ts b/packages/gitbook-v2/src/lib/data/memoize.ts index 442a4b76ea..e6cac10896 100644 --- a/packages/gitbook-v2/src/lib/data/memoize.ts +++ b/packages/gitbook-v2/src/lib/data/memoize.ts @@ -3,42 +3,43 @@ * With a logic to work per-request in Cloudflare Workers. */ export function withoutConcurrentExecution( - getGlobalContext: () => object | null | undefined, + _getGlobalContext: () => object | null | undefined, wrapped: (key: string, ...args: ArgsType) => Promise ): (cacheKey: string, ...args: ArgsType) => Promise { - const globalPromiseCache = new WeakMap>>(); + return wrapped; + // const globalPromiseCache = new WeakMap>>(); - return (key: string, ...args: ArgsType) => { - const globalContext = getGlobalContext() ?? globalThis; - console.log('globalContext', globalContext); + // return (key: string, ...args: ArgsType) => { + // const globalContext = getGlobalContext() ?? globalThis; + // console.log('globalContext', globalContext); - /** - * Cache storage that is scoped to the current request when executed in Cloudflare Workers, - * to avoid "Cannot perform I/O on behalf of a different request" errors. - */ - const promiseCache = - globalPromiseCache.get(globalContext) ?? new Map>(); - globalPromiseCache.set(globalContext, promiseCache); + // /** + // * Cache storage that is scoped to the current request when executed in Cloudflare Workers, + // * to avoid "Cannot perform I/O on behalf of a different request" errors. + // */ + // const promiseCache = + // globalPromiseCache.get(globalContext) ?? new Map>(); + // globalPromiseCache.set(globalContext, promiseCache); - const concurrent = promiseCache.get(key); - if (concurrent) { - console.log('returning concurrent value for', key); - return concurrent; - } + // const concurrent = promiseCache.get(key); + // if (concurrent) { + // console.log('returning concurrent value for', key); + // return concurrent; + // } - const promise = (async () => { - try { - const result = await wrapped(key, ...args); - return result; - } finally { - promiseCache.delete(key); - } - })(); + // const promise = (async () => { + // try { + // const result = await wrapped(key, ...args); + // return result; + // } finally { + // promiseCache.delete(key); + // } + // })(); - promiseCache.set(key, promise); + // promiseCache.set(key, promise); - return promise; - }; + // return promise; + // }; } /** From e9bfb7397e54f636ac9b47e2c4bda90c65243847 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 14:31:56 +0200 Subject: [PATCH 08/29] Log trace error --- packages/gitbook/src/lib/tracing.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/gitbook/src/lib/tracing.ts b/packages/gitbook/src/lib/tracing.ts index 38db339240..71b5c072e8 100644 --- a/packages/gitbook/src/lib/tracing.ts +++ b/packages/gitbook/src/lib/tracing.ts @@ -28,19 +28,19 @@ export async function trace( }; const start = now(); - let failed = false; + let traceError: null | Error = null; try { return await fn(span); } catch (error) { span.setAttribute('error', true); - failed = true; + traceError = error as Error; throw error; } finally { if (process.env.SILENT !== 'true' && process.env.NODE_ENV !== 'development') { const end = now(); // biome-ignore lint/suspicious/noConsole: we want to log performance data console.log( - `trace ${completeName} ${failed ? 'failed' : 'succeeded'} in ${end - start}ms`, + `trace ${completeName} ${traceError ? `failed with ${traceError.message}` : 'succeeded'} in ${end - start}ms`, attributes ); } From 0559c561e323637cc538f01d6e2212ebea137a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 14:47:18 +0200 Subject: [PATCH 09/29] Disable next cache on all requests --- packages/gitbook-v2/src/lib/data/api.ts | 136 +++++++++++++++++++----- 1 file changed, 109 insertions(+), 27 deletions(-) diff --git a/packages/gitbook-v2/src/lib/data/api.ts b/packages/gitbook-v2/src/lib/data/api.ts index b71e4cb186..7a2d03d16d 100644 --- a/packages/gitbook-v2/src/lib/data/api.ts +++ b/packages/gitbook-v2/src/lib/data/api.ts @@ -21,6 +21,15 @@ interface DataFetcherInput { apiToken: string | null; } +/** + * Options to pass to the `fetch` call to disable the Next data-cache when wrapped in `use cache`. + */ +export const noCacheFetchOptions: Partial = { + next: { + revalidate: 0, + }, +}; + /** * Create a data fetcher using an API token. * The data are being cached by Next.js built-in cache. @@ -196,7 +205,9 @@ const getUserById = withCacheKey( return trace(`getUserById(${params.userId})`, async () => { return wrapDataFetcherError(async () => { const api = apiClient(input); - const res = await api.users.getUserById(params.userId); + const res = await api.users.getUserById(params.userId, { + ...noCacheFetchOptions, + }); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('days'); return res.data; @@ -225,9 +236,15 @@ const getSpace = withCacheKey( return trace(`getSpace(${params.spaceId}, ${params.shareKey})`, async () => { return wrapDataFetcherError(async () => { const api = apiClient(input); - const res = await api.spaces.getSpaceById(params.spaceId, { - shareKey: params.shareKey, - }); + const res = await api.spaces.getSpaceById( + params.spaceId, + { + shareKey: params.shareKey, + }, + { + ...noCacheFetchOptions, + } + ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('days'); return res.data; @@ -261,7 +278,10 @@ const getChangeRequest = withCacheKey( const api = apiClient(input); const res = await api.spaces.getChangeRequestById( params.spaceId, - params.changeRequestId + params.changeRequestId, + { + ...noCacheFetchOptions, + } ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('minutes'); @@ -290,6 +310,9 @@ const getRevision = withCacheKey( params.revisionId, { metadata: params.metadata, + }, + { + ...noCacheFetchOptions, } ); cacheTag(...getCacheTagsFromResponse(res)); @@ -318,6 +341,9 @@ const getRevisionPages = withCacheKey( params.revisionId, { metadata: params.metadata, + }, + { + ...noCacheFetchOptions, } ); cacheTag(...getCacheTagsFromResponse(res)); @@ -347,7 +373,10 @@ const getRevisionFile = withCacheKey( params.spaceId, params.revisionId, params.fileId, - {} + {}, + { + ...noCacheFetchOptions, + } ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('max'); @@ -379,6 +408,9 @@ const getRevisionPageMarkdown = withCacheKey( params.pageId, { format: 'markdown', + }, + { + ...noCacheFetchOptions, } ); @@ -415,7 +447,10 @@ const getRevisionPageByPath = withCacheKey( params.spaceId, params.revisionId, encodedPath, - {} + {}, + { + ...noCacheFetchOptions, + } ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('max'); @@ -438,7 +473,10 @@ const getDocument = withCacheKey( const res = await api.spaces.getDocumentById( params.spaceId, params.documentId, - {} + {}, + { + ...noCacheFetchOptions, + } ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('max'); @@ -478,10 +516,17 @@ const getComputedDocument = withCacheKey( async () => { return wrapDataFetcherError(async () => { const api = apiClient(input); - const res = await api.spaces.getComputedDocument(params.spaceId, { - source: params.source, - seed: params.seed, - }); + const res = await api.spaces.getComputedDocument( + params.spaceId, + { + source: params.source, + seed: params.seed, + }, + {}, + { + ...noCacheFetchOptions, + } + ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('max'); return res.data; @@ -509,7 +554,11 @@ const getReusableContent = withCacheKey( const res = await api.spaces.getReusableContentInRevisionById( params.spaceId, params.revisionId, - params.reusableContentId + params.reusableContentId, + {}, + { + ...noCacheFetchOptions, + } ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('max'); @@ -541,7 +590,10 @@ const getLatestOpenAPISpecVersionContent = withCacheKey( const api = apiClient(input); const res = await api.orgs.getLatestOpenApiSpecVersionContent( params.organizationId, - params.slug + params.slug, + { + ...noCacheFetchOptions, + } ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('max'); @@ -579,6 +631,9 @@ const getPublishedContentSite = withCacheKey( params.siteId, { shareKey: params.siteShareKey, + }, + { + ...noCacheFetchOptions, } ); cacheTag(...getCacheTagsFromResponse(res)); @@ -623,6 +678,9 @@ const getSiteRedirectBySource = withCacheKey( { shareKey: params.siteShareKey, source: params.source, + }, + { + ...noCacheFetchOptions, } ); cacheTag(...getCacheTagsFromResponse(res)); @@ -650,9 +708,15 @@ const getEmbedByUrl = withCacheKey( return trace(`getEmbedByUrl(${params.spaceId}, ${params.url})`, async () => { return wrapDataFetcherError(async () => { const api = apiClient(input); - const res = await api.spaces.getEmbedByUrlInSpace(params.spaceId, { - url: params.url, - }); + const res = await api.spaces.getEmbedByUrlInSpace( + params.spaceId, + { + url: params.url, + }, + { + ...noCacheFetchOptions, + } + ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('weeks'); return res.data; @@ -684,10 +748,18 @@ const searchSiteContent = withCacheKey( return wrapDataFetcherError(async () => { const { organizationId, siteId, query, scope } = params; const api = apiClient(input); - const res = await api.orgs.searchSiteContent(organizationId, siteId, { - query, - ...scope, - }); + const res = await api.orgs.searchSiteContent( + organizationId, + siteId, + { + query, + ...scope, + }, + {}, + { + ...noCacheFetchOptions, + } + ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('hours'); return res.data.items; @@ -719,7 +791,10 @@ const renderIntegrationUi = withCacheKey( const api = apiClient(input); const res = await api.integrations.renderIntegrationUiWithPost( params.integrationName, - params.request + params.request, + { + ...noCacheFetchOptions, + } ); cacheTag(...getCacheTagsFromResponse(res)); cacheLife('days'); @@ -735,11 +810,18 @@ async function* streamAIResponse( params: Parameters[0] ) { const api = apiClient(input); - const res = await api.orgs.streamAiResponseInSite(params.organizationId, params.siteId, { - input: params.input, - output: params.output, - model: params.model, - }); + const res = await api.orgs.streamAiResponseInSite( + params.organizationId, + params.siteId, + { + input: params.input, + output: params.output, + model: params.model, + }, + { + ...noCacheFetchOptions, + } + ); for await (const event of res) { yield event; From 64bd602eefd3f8cf330f47897797ae9390d57b31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 18:28:37 +0200 Subject: [PATCH 10/29] Enable dynamicIO --- packages/gitbook-v2/next.config.mjs | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/gitbook-v2/next.config.mjs b/packages/gitbook-v2/next.config.mjs index d915cf312d..ee6b36d0c8 100644 --- a/packages/gitbook-v2/next.config.mjs +++ b/packages/gitbook-v2/next.config.mjs @@ -8,6 +8,7 @@ const nextConfig = { // This is needed to throw "forbidden" when the api token expired during revalidation authInterrupts: true, useCache: true, + dynamicIO: true, // Content is fully static, we can cache it in the session memory cache for a long time staleTimes: { From 0d8e7598f87090aaddc91d0b61de8bc5d38feccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 18:51:35 +0200 Subject: [PATCH 11/29] Try dynamicIO --- .../static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx index a35ecb69c8..4956912eea 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx @@ -7,8 +7,6 @@ import { type RouteParams, getPagePathFromParams, getStaticSiteContext } from '@ import type { Metadata, Viewport } from 'next'; -export const dynamic = 'force-static'; - type PageProps = { params: Promise; }; From 3eeaf949047c4a47734529c95b3ea51bb0b7d5dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Thu, 1 May 2025 19:00:34 +0200 Subject: [PATCH 12/29] Remove all force-static as a test --- .../sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts | 2 -- .../static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts | 2 -- .../[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts | 2 -- .../static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts | 2 -- .../static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts | 2 -- .../[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts | 2 -- .../[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts | 2 -- 7 files changed, 14 deletions(-) diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts index 80343b911a..1221610964 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts @@ -3,8 +3,6 @@ import type { NextRequest } from 'next/server'; import { serveLLMsTxt } from '@/routes/llms'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; -export const dynamic = 'force-static'; - export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts index 95820d768f..2bc8a98332 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts @@ -3,8 +3,6 @@ import type { NextRequest } from 'next/server'; import { serveRobotsTxt } from '@/routes/robots'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; -export const dynamic = 'force-static'; - export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts index 472ae6309a..ee376f3acb 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts @@ -3,8 +3,6 @@ import type { NextRequest } from 'next/server'; import { servePagesSitemap } from '@/routes/sitemap'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; -export const dynamic = 'force-static'; - export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts index eac5149cd9..ff2277af2b 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts @@ -3,8 +3,6 @@ import type { NextRequest } from 'next/server'; import { serveRootSitemap } from '@/routes/sitemap'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; -export const dynamic = 'force-static'; - export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts index 050aef9ef9..72243ef7c4 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts @@ -3,8 +3,6 @@ import type { NextRequest } from 'next/server'; import { serveIcon } from '@/routes/icon'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; -export const dynamic = 'force-static'; - export async function GET( request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts index 311a3a0b13..891281b3e9 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts @@ -2,8 +2,6 @@ import { servePageMarkdown } from '@/routes/markdownPage'; import { type RouteParams, getPagePathFromParams, getStaticSiteContext } from '@v2/app/utils'; import type { NextRequest } from 'next/server'; -export const dynamic = 'force-static'; - export async function GET(_request: NextRequest, { params }: { params: Promise }) { const { context } = await getStaticSiteContext(await params); const pathname = getPagePathFromParams(await params); diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts index 211de17274..198dd1144d 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts @@ -4,8 +4,6 @@ import type { PageIdParams } from '@/components/SitePage'; import { serveOGImage } from '@/routes/ogimage'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; -export const dynamic = 'force-static'; - export async function GET( _request: NextRequest, { params }: { params: Promise } From 13613a2db10f69cfd4ea9c77423f9d625c970536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 6 May 2025 11:46:09 +0200 Subject: [PATCH 13/29] Revert "Remove all force-static as a test" This reverts commit 3eeaf949047c4a47734529c95b3ea51bb0b7d5dd. --- .../sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts | 2 ++ .../static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts | 2 ++ .../[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts | 2 ++ .../static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts | 2 ++ .../static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts | 2 ++ .../[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts | 2 ++ .../[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts | 2 ++ 7 files changed, 14 insertions(+) diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts index 1221610964..80343b911a 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/llms.txt/route.ts @@ -3,6 +3,8 @@ import type { NextRequest } from 'next/server'; import { serveLLMsTxt } from '@/routes/llms'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; +export const dynamic = 'force-static'; + export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts index 2bc8a98332..95820d768f 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/robots.txt/route.ts @@ -3,6 +3,8 @@ import type { NextRequest } from 'next/server'; import { serveRobotsTxt } from '@/routes/robots'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; +export const dynamic = 'force-static'; + export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts index ee376f3acb..472ae6309a 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap-pages.xml/route.ts @@ -3,6 +3,8 @@ import type { NextRequest } from 'next/server'; import { servePagesSitemap } from '@/routes/sitemap'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; +export const dynamic = 'force-static'; + export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts index ff2277af2b..eac5149cd9 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/sitemap.xml/route.ts @@ -3,6 +3,8 @@ import type { NextRequest } from 'next/server'; import { serveRootSitemap } from '@/routes/sitemap'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; +export const dynamic = 'force-static'; + export async function GET( _request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts index 72243ef7c4..050aef9ef9 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/icon/route.ts @@ -3,6 +3,8 @@ import type { NextRequest } from 'next/server'; import { serveIcon } from '@/routes/icon'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; +export const dynamic = 'force-static'; + export async function GET( request: NextRequest, { params }: { params: Promise } diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts index 891281b3e9..311a3a0b13 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/markdown/[pagePath]/route.ts @@ -2,6 +2,8 @@ import { servePageMarkdown } from '@/routes/markdownPage'; import { type RouteParams, getPagePathFromParams, getStaticSiteContext } from '@v2/app/utils'; import type { NextRequest } from 'next/server'; +export const dynamic = 'force-static'; + export async function GET(_request: NextRequest, { params }: { params: Promise }) { const { context } = await getStaticSiteContext(await params); const pathname = getPagePathFromParams(await params); diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts index 198dd1144d..211de17274 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/~gitbook/ogimage/[pageId]/route.ts @@ -4,6 +4,8 @@ import type { PageIdParams } from '@/components/SitePage'; import { serveOGImage } from '@/routes/ogimage'; import { type RouteLayoutParams, getStaticSiteContext } from '@v2/app/utils'; +export const dynamic = 'force-static'; + export async function GET( _request: NextRequest, { params }: { params: Promise } From b2087a9a95903fc552104f34b65447075c7fe197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 6 May 2025 11:46:14 +0200 Subject: [PATCH 14/29] Revert "Try dynamicIO" This reverts commit 0d8e7598f87090aaddc91d0b61de8bc5d38feccf. --- .../static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx index 4956912eea..a35ecb69c8 100644 --- a/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx +++ b/packages/gitbook-v2/src/app/sites/static/[mode]/[siteURL]/[siteData]/[pagePath]/page.tsx @@ -7,6 +7,8 @@ import { type RouteParams, getPagePathFromParams, getStaticSiteContext } from '@ import type { Metadata, Viewport } from 'next'; +export const dynamic = 'force-static'; + type PageProps = { params: Promise; }; From 9265019c01fa256c3a498272c1335de91a5ae077 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 6 May 2025 11:46:18 +0200 Subject: [PATCH 15/29] Revert "Enable dynamicIO" This reverts commit 64bd602eefd3f8cf330f47897797ae9390d57b31. --- packages/gitbook-v2/next.config.mjs | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/gitbook-v2/next.config.mjs b/packages/gitbook-v2/next.config.mjs index ee6b36d0c8..d915cf312d 100644 --- a/packages/gitbook-v2/next.config.mjs +++ b/packages/gitbook-v2/next.config.mjs @@ -8,7 +8,6 @@ const nextConfig = { // This is needed to throw "forbidden" when the api token expired during revalidation authInterrupts: true, useCache: true, - dynamicIO: true, // Content is fully static, we can cache it in the session memory cache for a long time staleTimes: { From 6cc69028cd064e4f15a5754df7c4dcc7b6dbbec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 6 May 2025 11:51:55 +0200 Subject: [PATCH 16/29] Update --- bun.lock | 28 ++++++++++++++-------------- packages/gitbook-v2/package.json | 4 ++-- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/bun.lock b/bun.lock index 38ae35c920..37453a70c0 100644 --- a/bun.lock +++ b/bun.lock @@ -148,7 +148,7 @@ "@sindresorhus/fnv1a": "^3.1.0", "assert-never": "^1.2.1", "jwt-decode": "^4.0.0", - "next": "canary", + "next": "^15.3.1", "react": "^19.0.0", "react-dom": "^19.0.0", "rison": "^0.1.1", @@ -156,7 +156,7 @@ "warn-once": "^0.1.1", }, "devDependencies": { - "@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@a8815be", + "@opennextjs/cloudflare": "^1.0.0-beta.4", "@types/rison": "^0.0.9", "gitbook": "*", "postcss": "^8", @@ -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=="], - "@opennextjs/aws": ["@opennextjs/aws@3.6.0", "", { "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-fLhPgg9ftV8GKNxuoeRJevKa+LIGMMzY9LIGni+5FO9GyhVf0V93j0WTf2x/g9yk5I64hukMWmCqTszitaZ5yg=="], + "@opennextjs/aws": ["@opennextjs/aws@3.5.7", "", { "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-YjyHJrkIHI7YwQRCp8GjDOudu86oOc1RiwxvBBpPHrplsS18H4ZmkzGggAKhK6B4myGsJQ/q9kNP2TraoZiNzg=="], - "@opennextjs/cloudflare": ["@opennextjs/cloudflare@https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@a8815be", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.6.0", "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" } }], + "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.0-beta.4", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.5.7", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.7.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-/nEFnjrn8oxfZgW03ZgMzwJvcQ/U/MuXCTIABYvIGnja1+KxFZEc46GFGSxjTwdz4Hmt2eutaEGpQOqC6uRR3w=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], @@ -4077,7 +4077,7 @@ "gaxios/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "gitbook-v2/next": ["next@15.4.0-canary.17", "", { "dependencies": { "@next/env": "15.4.0-canary.17", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.17", "@next/swc-darwin-x64": "15.4.0-canary.17", "@next/swc-linux-arm64-gnu": "15.4.0-canary.17", "@next/swc-linux-arm64-musl": "15.4.0-canary.17", "@next/swc-linux-x64-gnu": "15.4.0-canary.17", "@next/swc-linux-x64-musl": "15.4.0-canary.17", "@next/swc-win32-arm64-msvc": "15.4.0-canary.17", "@next/swc-win32-x64-msvc": "15.4.0-canary.17", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-AH14xNNvz+6S33AB3TR+VHkS+cnhrzkNeh8TnYpSbtouQqhJtm0lTYjnQTGqa3cHGKMgLI1h13MU724HiJGbcg=="], + "gitbook-v2/next": ["next@15.3.1", "", { "dependencies": { "@next/env": "15.3.1", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.1", "@next/swc-darwin-x64": "15.3.1", "@next/swc-linux-arm64-gnu": "15.3.1", "@next/swc-linux-arm64-musl": "15.3.1", "@next/swc-linux-x64-gnu": "15.3.1", "@next/swc-linux-x64-musl": "15.3.1", "@next/swc-win32-arm64-msvc": "15.3.1", "@next/swc-win32-x64-msvc": "15.3.1", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g=="], "global-dirs/ini": ["ini@1.3.7", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="], @@ -4969,23 +4969,23 @@ "gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], - "gitbook-v2/next/@next/env": ["@next/env@15.4.0-canary.17", "", {}, "sha512-k0dqCJhBYqPyRr/i+M9OYEtn9nJuZjJf9y6jVOkwl+xaEr4K6UmIBu6vVfoWitDwPLRj4NgKmHgkH75dPRxAQg=="], + "gitbook-v2/next/@next/env": ["@next/env@15.3.1", "", {}, "sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ=="], - "gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.17", "", { "os": "darwin", "cpu": "arm64" }, "sha512-CX2ZV8DsLjbX6eYf0OhLMfjZnFykwzAcG6gnz9fJdU+l20YvYj5BlWAefSTLncQ6KWq/FLb8XU00PL8ThaGHPg=="], + "gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w=="], - "gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.17", "", { "os": "darwin", "cpu": "x64" }, "sha512-J2cdOuAAxpVbjk6QRHPtq1hbDXuNIgCGB/G4HvKot71VIKXg/Fb1IaxwFiBYNl0zsRgH5CPO+AO9pUUGwpVPyQ=="], + "gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g=="], - "gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-DKUs71Gi/leUH9RrMRamVfxXfOKdNGov7CH1KufeCyKIl9PNQQhP5GMgUk/19nvNmyFV7PjxIXRxElgBzRPtyQ=="], + "gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ=="], - "gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.17", "", { "os": "linux", "cpu": "arm64" }, "sha512-k4tjv818/f2DLmCZOkqTEsnqDpIUeKFb1gH9WnexjfAJLD2RXmb+hde8PDB64/Syf2RdytREdPZBnDs2okxrbQ=="], + "gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg=="], - "gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.17", "", { "os": "linux", "cpu": "x64" }, "sha512-5vLVdcMKG2iMPTUu0E9aAvVBbEwKJeCA9CGb1hsQuEuTA3N3XntRHvZKj32POTE4jYJeb0DnxEZHli7SiyOBYw=="], + "gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A=="], - "gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.17", "", { "os": "linux", "cpu": "x64" }, "sha512-N5YAd9chWQdLJayfkgZP+d/daTAWKa2jm0SPNhjZiez88fkoZlBU+GPLsozA7L3RdaqLGqDPrvavylt1Iw0L9Q=="], + "gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.1", "", { "os": "linux", "cpu": "x64" }, "sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA=="], - "gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.17", "", { "os": "win32", "cpu": "arm64" }, "sha512-URAe7qnVC8tarSBx/V/WDRP3xzkb8tr3m9gEjIYbBBVF64xlzrPshcPe5HIssGjL/5u3NRs9VxgsE9xZ7CcxKA=="], + "gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw=="], - "gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.17", "", { "os": "win32", "cpu": "x64" }, "sha512-kEFB4sX2/LM/OpFl08TrV7SIntFJKOh4RfvdUFQ++OTSHDLZz+G24FtRPHx6b6ULWWdNndkY2WfBMH9qy9RfBQ=="], + "gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.1", "", { "os": "win32", "cpu": "x64" }, "sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ=="], "gitbook-v2/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json index a92ed9badf..0265f31a3e 100644 --- a/packages/gitbook-v2/package.json +++ b/packages/gitbook-v2/package.json @@ -3,7 +3,7 @@ "version": "0.2.5", "private": true, "dependencies": { - "next": "canary", + "next": "^15.3.1", "react": "^19.0.0", "react-dom": "^19.0.0", "@gitbook/api": "*", @@ -17,7 +17,7 @@ }, "devDependencies": { "gitbook": "*", - "@opennextjs/cloudflare": "https://pkg.pr.new/opennextjs/opennextjs-cloudflare/@opennextjs/cloudflare@a8815be", + "@opennextjs/cloudflare": "^1.0.0-beta.4", "@types/rison": "^0.0.9", "tailwindcss": "^3.4.0", "postcss": "^8" From 858b09344a952d084162d06680b86ccfad263c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 6 May 2025 22:40:14 +0200 Subject: [PATCH 17/29] Update to 1.0.0 --- bun.lock | 6 +++--- packages/gitbook-v2/package.json | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/bun.lock b/bun.lock index 37453a70c0..a2773a10dc 100644 --- a/bun.lock +++ b/bun.lock @@ -156,7 +156,7 @@ "warn-once": "^0.1.1", }, "devDependencies": { - "@opennextjs/cloudflare": "^1.0.0-beta.4", + "@opennextjs/cloudflare": "^1.0.0", "@types/rison": "^0.0.9", "gitbook": "*", "postcss": "^8", @@ -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=="], - "@opennextjs/aws": ["@opennextjs/aws@3.5.7", "", { "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-YjyHJrkIHI7YwQRCp8GjDOudu86oOc1RiwxvBBpPHrplsS18H4ZmkzGggAKhK6B4myGsJQ/q9kNP2TraoZiNzg=="], + "@opennextjs/aws": ["@opennextjs/aws@3.6.0", "", { "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-fLhPgg9ftV8GKNxuoeRJevKa+LIGMMzY9LIGni+5FO9GyhVf0V93j0WTf2x/g9yk5I64hukMWmCqTszitaZ5yg=="], - "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.0-beta.4", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.5.7", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^4.7.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-/nEFnjrn8oxfZgW03ZgMzwJvcQ/U/MuXCTIABYvIGnja1+KxFZEc46GFGSxjTwdz4Hmt2eutaEGpQOqC6uRR3w=="], + "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.0", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "~3.6.0", "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-/uOeP7Qqjfw+6FWt7H5ufLZG8iB11CCFnPv1Yzjj3WrypgGQLDafW/3ri6DiKdQcWm3JdR5Fdp7cWp8sjz1F5Q=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json index 0265f31a3e..1e6566d00e 100644 --- a/packages/gitbook-v2/package.json +++ b/packages/gitbook-v2/package.json @@ -17,7 +17,7 @@ }, "devDependencies": { "gitbook": "*", - "@opennextjs/cloudflare": "^1.0.0-beta.4", + "@opennextjs/cloudflare": "^1.0.0", "@types/rison": "^0.0.9", "tailwindcss": "^3.4.0", "postcss": "^8" From 90fb799ec6a7b61b3fc123c89d7fc0ec8c0bebe7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 6 May 2025 22:40:52 +0200 Subject: [PATCH 18/29] Rollback --- packages/gitbook-v2/src/lib/data/memoize.ts | 55 ++++++++++----------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/packages/gitbook-v2/src/lib/data/memoize.ts b/packages/gitbook-v2/src/lib/data/memoize.ts index e6cac10896..ff1ff5e841 100644 --- a/packages/gitbook-v2/src/lib/data/memoize.ts +++ b/packages/gitbook-v2/src/lib/data/memoize.ts @@ -3,43 +3,40 @@ * With a logic to work per-request in Cloudflare Workers. */ export function withoutConcurrentExecution( - _getGlobalContext: () => object | null | undefined, + getGlobalContext: () => object | null | undefined, wrapped: (key: string, ...args: ArgsType) => Promise ): (cacheKey: string, ...args: ArgsType) => Promise { - return wrapped; - // const globalPromiseCache = new WeakMap>>(); + const globalPromiseCache = new WeakMap>>(); - // return (key: string, ...args: ArgsType) => { - // const globalContext = getGlobalContext() ?? globalThis; - // console.log('globalContext', globalContext); + return (key: string, ...args: ArgsType) => { + const globalContext = getGlobalContext() ?? globalThis; - // /** - // * Cache storage that is scoped to the current request when executed in Cloudflare Workers, - // * to avoid "Cannot perform I/O on behalf of a different request" errors. - // */ - // const promiseCache = - // globalPromiseCache.get(globalContext) ?? new Map>(); - // globalPromiseCache.set(globalContext, promiseCache); + /** + * Cache storage that is scoped to the current request when executed in Cloudflare Workers, + * to avoid "Cannot perform I/O on behalf of a different request" errors. + */ + const promiseCache = + globalPromiseCache.get(globalContext) ?? new Map>(); + globalPromiseCache.set(globalContext, promiseCache); - // const concurrent = promiseCache.get(key); - // if (concurrent) { - // console.log('returning concurrent value for', key); - // return concurrent; - // } + const concurrent = promiseCache.get(key); + if (concurrent) { + return concurrent; + } - // const promise = (async () => { - // try { - // const result = await wrapped(key, ...args); - // return result; - // } finally { - // promiseCache.delete(key); - // } - // })(); + const promise = (async () => { + try { + const result = await wrapped(key, ...args); + return result; + } finally { + promiseCache.delete(key); + } + })(); - // promiseCache.set(key, promise); + promiseCache.set(key, promise); - // return promise; - // }; + return promise; + }; } /** From a3841f9a6a3e4b163e04ba2a39a5574e24931600 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Tue, 6 May 2025 22:46:12 +0200 Subject: [PATCH 19/29] Optimize performances --- packages/gitbook-v2/open-next.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/gitbook-v2/open-next.config.ts b/packages/gitbook-v2/open-next.config.ts index 2ce66f794b..4ff4f74e88 100644 --- a/packages/gitbook-v2/open-next.config.ts +++ b/packages/gitbook-v2/open-next.config.ts @@ -23,4 +23,7 @@ export default defineCloudflareConfig({ filterFn: softTagFilter, }), queue: doQueue, + + // Performance improvements as we don't use PPR + enableCacheInterception: true, }); From 76ea53dda722699bb78a4036a0c7e98f6954b4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 9 May 2025 13:01:41 +0200 Subject: [PATCH 20/29] bun install --- bun.lock | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/bun.lock b/bun.lock index 6e27a46b06..ecab02d38e 100644 --- a/bun.lock +++ b/bun.lock @@ -148,7 +148,7 @@ "@sindresorhus/fnv1a": "^3.1.0", "assert-never": "^1.2.1", "jwt-decode": "^4.0.0", - "next": "canary", + "next": "^15.3.1", "react": "^19.0.0", "react-dom": "^19.0.0", "rison": "^0.1.1", @@ -156,7 +156,7 @@ "warn-once": "^0.1.1", }, "devDependencies": { - "@opennextjs/cloudflare": "^1.0.0-beta.3", + "@opennextjs/cloudflare": "^1.0.1", "@types/rison": "^0.0.9", "gitbook": "*", "postcss": "^8", @@ -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=="], - "@opennextjs/aws": ["@opennextjs/aws@3.5.7", "", { "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-YjyHJrkIHI7YwQRCp8GjDOudu86oOc1RiwxvBBpPHrplsS18H4ZmkzGggAKhK6B4myGsJQ/q9kNP2TraoZiNzg=="], + "@opennextjs/aws": ["@opennextjs/aws@3.6.0", "", { "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-fLhPgg9ftV8GKNxuoeRJevKa+LIGMMzY9LIGni+5FO9GyhVf0V93j0WTf2x/g9yk5I64hukMWmCqTszitaZ5yg=="], - "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.0-beta.3", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.5.7", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^3.114.3 || ^4.7.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-qKBXQZhUeQ+iGvfJeF7PO30g59LHnPOlRVZd77zxwn6Uc9C+c0LSwo8N28XRIWyQPkY007rKk9pSIxOrP4MHtQ=="], + "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.1", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "~3.6.0", "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-fcClRf6ktPW+w5XRXjkbGk/WWxbHSv6+DtfDHP4UGQkEQl7LRSR+f0ZSlAj5dHI2KvMrpFtzHr8durEFqgQBYg=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], @@ -4077,7 +4077,7 @@ "gaxios/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "gitbook-v2/next": ["next@15.4.0-canary.26", "", { "dependencies": { "@next/env": "15.4.0-canary.26", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.4.0-canary.26", "@next/swc-darwin-x64": "15.4.0-canary.26", "@next/swc-linux-arm64-gnu": "15.4.0-canary.26", "@next/swc-linux-arm64-musl": "15.4.0-canary.26", "@next/swc-linux-x64-gnu": "15.4.0-canary.26", "@next/swc-linux-x64-musl": "15.4.0-canary.26", "@next/swc-win32-arm64-msvc": "15.4.0-canary.26", "@next/swc-win32-x64-msvc": "15.4.0-canary.26", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-0lq0x+H4ewc6vXth3S9shrcK3eYl+4wLXQqdboVwBbJe0ykB3+QbGdXFIEICCZsmbAOaii0ag0tzqD3y/vr3bw=="], + "gitbook-v2/next": ["next@15.3.2", "", { "dependencies": { "@next/env": "15.3.2", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.2", "@next/swc-darwin-x64": "15.3.2", "@next/swc-linux-arm64-gnu": "15.3.2", "@next/swc-linux-arm64-musl": "15.3.2", "@next/swc-linux-x64-gnu": "15.3.2", "@next/swc-linux-x64-musl": "15.3.2", "@next/swc-win32-arm64-msvc": "15.3.2", "@next/swc-win32-x64-msvc": "15.3.2", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ=="], "global-dirs/ini": ["ini@1.3.7", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="], @@ -4969,23 +4969,23 @@ "gaxios/https-proxy-agent/debug": ["debug@4.3.7", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ=="], - "gitbook-v2/next/@next/env": ["@next/env@15.4.0-canary.26", "", {}, "sha512-+WeMYRfTZWaosbIAjuNESPVjynDz/NKukoR7mF/u3Wuwr40KgScpxD0IuU0T7XbPfprnaInSKAylufFvrXRh+A=="], + "gitbook-v2/next/@next/env": ["@next/env@15.3.2", "", {}, "sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g=="], - "gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.4.0-canary.26", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HxtmV8Uoai8Z4wAU1tFWzASogAS+xVVP5Z5frbFu0yQ+1ocb9xQTjNqhiD5xPSAU8pNGWasCod8tlTCBzJzHQg=="], + "gitbook-v2/next/@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@15.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2DR6kY/OGcokbnCsjHpNeQblqCZ85/1j6njYSkzRdpLn5At7OkSdmk7WyAmB9G0k25+VgqVZ/u356OSoQZ3z0g=="], - "gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.4.0-canary.26", "", { "os": "darwin", "cpu": "x64" }, "sha512-1MLiD1Bj6xSi5MkkQ8IK7A13KZJG9bzoWqdXT/tveVCinmYrl/zY7z/9dgvG+84gAE6uN4BGjp6f3IxRsvYDBA=="], + "gitbook-v2/next/@next/swc-darwin-x64": ["@next/swc-darwin-x64@15.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w=="], - "gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.4.0-canary.26", "", { "os": "linux", "cpu": "arm64" }, "sha512-cIVgFgOdMDbnPixR/u3ICW60/HlnDbACCb2O+p9+DJj7s1dsN63Cs9qxc9pDJb7tgL0BFPhYcmGeJfd/bZ4h7w=="], + "gitbook-v2/next/@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@15.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA=="], - "gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.4.0-canary.26", "", { "os": "linux", "cpu": "arm64" }, "sha512-o4YS6E3FD2DpZBDvUai9bPMLcpcNZ3THc2BzysSbZeARPiAQuKoudwPJoCpi2t7vajrvczpxBwTPG2uL05ypEA=="], + "gitbook-v2/next/@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@15.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg=="], - "gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.4.0-canary.26", "", { "os": "linux", "cpu": "x64" }, "sha512-M2/MFrQcPI7Ul5Fq5AOeoARrT0B9SrGiy7BLnPuE7Iai1+xkhfSsxIMF5JeDm/GfJnzcwA2oSvrOg0e7KKdaCA=="], + "gitbook-v2/next/@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@15.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg=="], - "gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.4.0-canary.26", "", { "os": "linux", "cpu": "x64" }, "sha512-p5JpQ7k/1LyBzNZglqA8JJm7GRmadPkTyHoWaqMxhiVdcQHGbjwsiNjjAtMNjetNOXxj8ebxjiBsAt+34Ak1IQ=="], + "gitbook-v2/next/@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@15.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w=="], - "gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.4.0-canary.26", "", { "os": "win32", "cpu": "arm64" }, "sha512-FlXIBNOSwnGxxN+HekUfz4Y0n4gPGzqcY3wa3p+5JhzFT7r0oCxMxOdRbs7w8jF5b6uSkWVIQXWFL43F6+8J4g=="], + "gitbook-v2/next/@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@15.3.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ=="], - "gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.4.0-canary.26", "", { "os": "win32", "cpu": "x64" }, "sha512-h9CKrDiEeBof+8IgHStYATYrKVuUt8ggy6429kViWlDbuY6gkuIplf3IRlfpdWAB32I1e4qqUVl/s2xRMgQdqg=="], + "gitbook-v2/next/@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@15.3.2", "", { "os": "win32", "cpu": "x64" }, "sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA=="], "gitbook-v2/next/postcss": ["postcss@8.4.31", "", { "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" } }, "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ=="], From 68afdaa0c3eeba2c902f151590160556c66eeb87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 9 May 2025 13:02:57 +0200 Subject: [PATCH 21/29] Rollback to unstable_cache to test --- packages/gitbook-v2/src/lib/data/api.ts | 1381 ++++++++++++++++------- 1 file changed, 977 insertions(+), 404 deletions(-) diff --git a/packages/gitbook-v2/src/lib/data/api.ts b/packages/gitbook-v2/src/lib/data/api.ts index 7a2d03d16d..20e495656e 100644 --- a/packages/gitbook-v2/src/lib/data/api.ts +++ b/packages/gitbook-v2/src/lib/data/api.ts @@ -7,8 +7,14 @@ import { type RenderIntegrationUI, } from '@gitbook/api'; import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags'; -import { GITBOOK_API_TOKEN, GITBOOK_API_URL, GITBOOK_USER_AGENT } from '@v2/lib/env'; +import { + GITBOOK_API_TOKEN, + GITBOOK_API_URL, + GITBOOK_RUNTIME, + GITBOOK_USER_AGENT, +} from '@v2/lib/env'; import { unstable_cacheLife as cacheLife, unstable_cacheTag as cacheTag } from 'next/cache'; +import { unstable_cache } from 'next/cache'; import { getCloudflareContext, getCloudflareRequestGlobal } from './cloudflare'; import { DataFetcherError, wrapDataFetcherError } from './errors'; import { withCacheKey, withoutConcurrentExecution } from './memoize'; @@ -22,13 +28,16 @@ interface DataFetcherInput { } /** - * Options to pass to the `fetch` call to disable the Next data-cache when wrapped in `use cache`. + * Revalidation profile for the cache. + * Based on https://nextjs.org/docs/app/api-reference/functions/cacheLife#default-cache-profiles */ -export const noCacheFetchOptions: Partial = { - next: { - revalidate: 0, - }, -}; +enum RevalidationProfile { + minutes = 60, + hours = 60 * 60, + days = 60 * 60 * 24, + weeks = 60 * 60 * 24 * 7, + max = 60 * 60 * 24 * 30, +} /** * Create a data fetcher using an API token. @@ -197,301 +206,589 @@ export function createDataFetcher( }; } +/* + * For the following functions, we: + * - Wrap them with `withCacheKey` to compute a cache key from the function arguments ONCE (to be performant) + * - Pass the cache key to `unstable_cache` to ensure the cache is not tied to closures + * - Call the uncached function in a `withoutConcurrentExecution` wrapper to prevent concurrent executions + * + * Important: + * - Only the function inside the `unstable_cache` is wrapped in `withoutConcurrentExecution` as Next.js needs to call + * the return of `unstable_cache` to identify the tags. + */ + const getUserById = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async (_, input: DataFetcherInput, params: { userId: string }) => { - 'use cache'; - return trace(`getUserById(${params.userId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.users.getUserById(params.userId, { - ...noCacheFetchOptions, - }); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - return res.data; - }); - }); + async (cacheKey, input: DataFetcherInput, params: { userId: string }) => { + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getUserByIdUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( + async () => { + return getUserByIdUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.days, + tags: [], + } + ); + + return uncached(); } ) ); +const getUserByIdUseCache = async (input: DataFetcherInput, params: { userId: string }) => { + 'use cache'; + return getUserByIdUncached(input, params, true); +}; + +const getUserByIdUncached = async ( + input: DataFetcherInput, + params: { userId: string }, + withUseCache = false +) => { + return trace(`getUserById.uncached(${params.userId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.users.getUserById(params.userId); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + } + return res.data; + }); + }); +}; + const getSpace = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; shareKey: string | undefined } ) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'space', - space: params.spaceId, - }) + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getSpaceUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( + async () => { + return getSpaceUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.days, + tags: [ + getCacheTag({ + tag: 'space', + space: params.spaceId, + }), + ], + } ); - return trace(`getSpace(${params.spaceId}, ${params.shareKey})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getSpaceById( - params.spaceId, - { - shareKey: params.shareKey, - }, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - return res.data; - }); - }); + return uncached(); } ) ); +const getSpaceUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; shareKey: string | undefined } +) => { + 'use cache'; + return getSpaceUncached(input, params, true); +}; + +const getSpaceUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; shareKey: string | undefined }, + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + getCacheTag({ + tag: 'space', + space: params.spaceId, + }) + ); + } + + return trace(`getSpace.uncached(${params.spaceId}, ${params.shareKey})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getSpaceById(params.spaceId, { + shareKey: params.shareKey, + }); + + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + } + return res.data; + }); + }); +}; + const getChangeRequest = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; changeRequestId: string } ) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'change-request', - space: params.spaceId, - changeRequest: params.changeRequestId, - }) - ); + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getChangeRequestUseCache(input, params); + } - return trace( - `getChangeRequest(${params.spaceId}, ${params.changeRequestId})`, + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getChangeRequestById( - params.spaceId, - params.changeRequestId, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('minutes'); - return res.data; - }); + return getChangeRequestUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.minutes * 5, + tags: [ + getCacheTag({ + tag: 'change-request', + space: params.spaceId, + changeRequest: params.changeRequestId, + }), + ], } ); + + return uncached(); } ) ); +const getChangeRequestUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; changeRequestId: string } +) => { + 'use cache'; + return getChangeRequestUncached(input, params, true); +}; + +const getChangeRequestUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; changeRequestId: string }, + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + getCacheTag({ + tag: 'change-request', + space: params.spaceId, + changeRequest: params.changeRequestId, + }) + ); + } + + return trace( + `getChangeRequest.uncached(${params.spaceId}, ${params.changeRequestId})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getChangeRequestById( + params.spaceId, + params.changeRequestId + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('minutes'); + } + return res.data; + }); + } + ); +}; + const getRevision = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - 'use cache'; - return trace(`getRevision(${params.spaceId}, ${params.revisionId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getRevisionById( - params.spaceId, - params.revisionId, - { - metadata: params.metadata, - }, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - return res.data; - }); - }); + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getRevisionUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( + async () => { + return getRevisionUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: [], + } + ); + + return uncached(); } ) ); +const getRevisionUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; metadata: boolean } +) => { + 'use cache'; + return getRevisionUncached(input, params, true); +}; + +const getRevisionUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; metadata: boolean }, + withUseCache = false +) => { + return trace(`getRevision.uncached(${params.spaceId}, ${params.revisionId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getRevisionById(params.spaceId, params.revisionId, { + metadata: params.metadata, + }); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + return res.data; + }); + }); +}; + const getRevisionPages = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - 'use cache'; - return trace(`getRevisionPages(${params.spaceId}, ${params.revisionId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.listPagesInRevisionById( - params.spaceId, - params.revisionId, - { - metadata: params.metadata, - }, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - return res.data.pages; - }); - }); + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getRevisionPagesUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( + async () => { + return getRevisionPagesUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: [], + } + ); + + return uncached(); } ) ); +const getRevisionPagesUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; metadata: boolean } +) => { + 'use cache'; + return getRevisionPagesUncached(input, params, true); +}; + +const getRevisionPagesUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; metadata: boolean }, + withUseCache = false +) => { + return trace(`getRevisionPages.uncached(${params.spaceId}, ${params.revisionId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.listPagesInRevisionById( + params.spaceId, + params.revisionId, + { + metadata: params.metadata, + } + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + return res.data.pages; + }); + }); +}; + const getRevisionFile = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; revisionId: string; fileId: string } ) => { - 'use cache'; - return trace( - `getRevisionFile(${params.spaceId}, ${params.revisionId}, ${params.fileId})`, + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getRevisionFileUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getFileInRevisionById( - params.spaceId, - params.revisionId, - params.fileId, - {}, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - return res.data; - }); + return getRevisionFileUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: [], } ); + + return uncached(); } ) ); +const getRevisionFileUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; fileId: string } +) => { + 'use cache'; + return getRevisionFileUncached(input, params, true); +}; + +const getRevisionFileUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; fileId: string }, + withUseCache = false +) => { + return trace( + `getRevisionFile.uncached(${params.spaceId}, ${params.revisionId}, ${params.fileId})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getFileInRevisionById( + params.spaceId, + params.revisionId, + params.fileId, + {} + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + return res.data; + }); + } + ); +}; + const getRevisionPageMarkdown = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; revisionId: string; pageId: string } ) => { - 'use cache'; - return trace( - `getRevisionPageMarkdown(${params.spaceId}, ${params.revisionId}, ${params.pageId})`, + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getRevisionPageMarkdownUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getPageInRevisionById( - params.spaceId, - params.revisionId, - params.pageId, - { - format: 'markdown', - }, - { - ...noCacheFetchOptions, - } - ); - - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - - if (!('markdown' in res.data)) { - throw new DataFetcherError('Page is not a document', 404); - } - return res.data.markdown; - }); + return getRevisionPageMarkdownUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: [], } ); + + return uncached(); } ) ); +const getRevisionPageMarkdownUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; pageId: string } +) => { + 'use cache'; + return getRevisionPageMarkdownUncached(input, params, true); +}; + +const getRevisionPageMarkdownUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; pageId: string }, + withUseCache = false +) => { + return trace( + `getRevisionPageMarkdown.uncached(${params.spaceId}, ${params.revisionId}, ${params.pageId})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getPageInRevisionById( + params.spaceId, + params.revisionId, + params.pageId, + { + format: 'markdown', + } + ); + + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + + if (!('markdown' in res.data)) { + throw new DataFetcherError('Page is not a document', 404); + } + return res.data.markdown; + }); + } + ); +}; + const getRevisionPageByPath = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; revisionId: string; path: string } ) => { - 'use cache'; - return trace( - `getRevisionPageByPath(${params.spaceId}, ${params.revisionId}, ${params.path})`, + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getRevisionPageByPathUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - const encodedPath = encodeURIComponent(params.path); - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getPageInRevisionByPath( - params.spaceId, - params.revisionId, - encodedPath, - {}, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - return res.data; - }); + return getRevisionPageByPathUncached(input, params); + }, + [cacheKey, 'v2'], + { + revalidate: RevalidationProfile.max, + tags: [], } ); + + return uncached(); } ) ); -const getDocument = withCacheKey( - withoutConcurrentExecution( - getCloudflareRequestGlobal, - async (_, input: DataFetcherInput, params: { spaceId: string; documentId: string }) => { - 'use cache'; - return trace(`getDocument(${params.spaceId}, ${params.documentId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getDocumentById( - params.spaceId, - params.documentId, - {}, - { - ...noCacheFetchOptions, - } - ); +const getRevisionPageByPathUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; path: string } +) => { + 'use cache'; + return getRevisionPageByPathUncached(input, params, true); +}; + +const getRevisionPageByPathUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; path: string }, + withUseCache = false +) => { + return trace( + `getRevisionPageByPath.uncached(${params.spaceId}, ${params.revisionId}, ${params.path})`, + async () => { + const encodedPath = encodeURIComponent(params.path); + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getPageInRevisionByPath( + params.spaceId, + params.revisionId, + encodedPath, + {} + ); + if (withUseCache) { cacheTag(...getCacheTagsFromResponse(res)); cacheLife('max'); - return res.data; - }); + } + return res.data; }); } + ); +}; + +const getDocument = withCacheKey( + withoutConcurrentExecution( + getCloudflareRequestGlobal, + async ( + cacheKey, + input: DataFetcherInput, + params: { spaceId: string; documentId: string } + ) => { + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getDocumentUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( + async () => { + return getDocumentUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: [], + } + ); + + return uncached(); + } ) ); +const getDocumentUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; documentId: string } +) => { + 'use cache'; + return getDocumentUncached(input, params, true); +}; + +const getDocumentUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; documentId: string }, + withUseCache = false +) => { + return trace(`getDocument.uncached(${params.spaceId}, ${params.documentId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getDocumentById(params.spaceId, params.documentId, {}); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + return res.data; + }); + }); +}; + const getComputedDocument = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; @@ -500,157 +797,309 @@ const getComputedDocument = withCacheKey( seed: string; } ) => { - 'use cache'; - cacheTag( - ...getComputedContentSourceCacheTags( - { - spaceId: params.spaceId, - organizationId: params.organizationId, - }, - params.source - ) - ); + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getComputedDocumentUseCache(input, params); + } - return trace( - `getComputedDocument(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`, + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getComputedDocument( - params.spaceId, - { - source: params.source, - seed: params.seed, - }, - {}, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - return res.data; - }); + return getComputedDocumentUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: getComputedContentSourceCacheTags( + { + spaceId: params.spaceId, + organizationId: params.organizationId, + }, + params.source + ), } ); + + return uncached(); } ) ); +const getComputedDocumentUseCache = async ( + input: DataFetcherInput, + params: { + spaceId: string; + organizationId: string; + source: ComputedContentSource; + seed: string; + } +) => { + 'use cache'; + return getComputedDocumentUncached(input, params, true); +}; + +const getComputedDocumentUncached = async ( + input: DataFetcherInput, + params: { + spaceId: string; + organizationId: string; + source: ComputedContentSource; + seed: string; + }, + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + ...getComputedContentSourceCacheTags( + { + spaceId: params.spaceId, + organizationId: params.organizationId, + }, + params.source + ) + ); + } + + return trace( + `getComputedDocument.uncached(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getComputedDocument(params.spaceId, { + source: params.source, + seed: params.seed, + }); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + return res.data; + }); + } + ); +}; + const getReusableContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { spaceId: string; revisionId: string; reusableContentId: string } ) => { - 'use cache'; - return trace( - `getReusableContent(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`, + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getReusableContentUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getReusableContentInRevisionById( - params.spaceId, - params.revisionId, - params.reusableContentId, - {}, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - return res.data; - }); + return getReusableContentUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: [], } ); + + return uncached(); } ) ); +const getReusableContentUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; reusableContentId: string } +) => { + 'use cache'; + return getReusableContentUncached(input, params, true); +}; + +const getReusableContentUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; revisionId: string; reusableContentId: string }, + withUseCache = false +) => { + return trace( + `getReusableContent.uncached(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getReusableContentInRevisionById( + params.spaceId, + params.revisionId, + params.reusableContentId + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + return res.data; + }); + } + ); +}; + const getLatestOpenAPISpecVersionContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async (_, input: DataFetcherInput, params: { organizationId: string; slug: string }) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'openapi', - organization: params.organizationId, - openAPISpec: params.slug, - }) - ); + async ( + cacheKey, + input: DataFetcherInput, + params: { organizationId: string; slug: string } + ) => { + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getLatestOpenAPISpecVersionContentUseCache(input, params); + } - return trace( - `getLatestOpenAPISpecVersionContent(${params.organizationId}, ${params.slug})`, + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getLatestOpenApiSpecVersionContent( - params.organizationId, - params.slug, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - return res.data; - }); + return getLatestOpenAPISpecVersionContentUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.max, + tags: [ + getCacheTag({ + tag: 'openapi', + organization: params.organizationId, + openAPISpec: params.slug, + }), + ], } ); + + return uncached(); } ) ); +const getLatestOpenAPISpecVersionContentUseCache = async ( + input: DataFetcherInput, + params: { organizationId: string; slug: string } +) => { + 'use cache'; + return getLatestOpenAPISpecVersionContentUncached(input, params, true); +}; + +const getLatestOpenAPISpecVersionContentUncached = async ( + input: DataFetcherInput, + params: { organizationId: string; slug: string }, + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + getCacheTag({ + tag: 'openapi', + organization: params.organizationId, + openAPISpec: params.slug, + }) + ); + } + + return trace( + `getLatestOpenAPISpecVersionContent.uncached(${params.organizationId}, ${params.slug})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getLatestOpenApiSpecVersionContent( + params.organizationId, + params.slug + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + } + return res.data; + }); + } + ); +}; + const getPublishedContentSite = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { organizationId: string; siteId: string; siteShareKey: string | undefined } ) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getPublishedContentSiteUseCache(input, params); + } - return trace( - `getPublishedContentSite(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`, + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getPublishedContentSite( - params.organizationId, - params.siteId, - { - shareKey: params.siteShareKey, - }, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - return res.data; - }); + return getPublishedContentSiteUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.days, + tags: [ + getCacheTag({ + tag: 'site', + site: params.siteId, + }), + ], } ); + + return uncached(); } ) ); +const getPublishedContentSiteUseCache = async ( + input: DataFetcherInput, + params: { organizationId: string; siteId: string; siteShareKey: string | undefined } +) => { + 'use cache'; + return getPublishedContentSiteUncached(input, params, true); +}; + +const getPublishedContentSiteUncached = async ( + input: DataFetcherInput, + params: { organizationId: string; siteId: string; siteShareKey: string | undefined }, + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); + } + + return trace( + `getPublishedContentSite.uncached(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getPublishedContentSite( + params.organizationId, + params.siteId, + { + shareKey: params.siteShareKey, + } + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + } + return res.data; + }); + } + ); +}; + const getSiteRedirectBySource = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { organizationId: string; @@ -659,169 +1108,293 @@ const getSiteRedirectBySource = withCacheKey( source: string; } ) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getSiteRedirectBySourceUseCache(input, params); + } - return trace( - `getSiteRedirectBySource(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`, + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getSiteRedirectBySource( - params.organizationId, - params.siteId, - { - shareKey: params.siteShareKey, - source: params.source, - }, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - return res.data; - }); + return getSiteRedirectBySourceUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.days, + tags: [ + getCacheTag({ + tag: 'site', + site: params.siteId, + }), + ], } ); + + return uncached(); } ) ); +const getSiteRedirectBySourceUseCache = async ( + input: DataFetcherInput, + params: { + organizationId: string; + siteId: string; + siteShareKey: string | undefined; + source: string; + } +) => { + 'use cache'; + return getSiteRedirectBySourceUncached(input, params, true); +}; + +const getSiteRedirectBySourceUncached = async ( + input: DataFetcherInput, + params: { + organizationId: string; + siteId: string; + siteShareKey: string | undefined; + source: string; + }, + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); + } + + return trace( + `getSiteRedirectBySource.uncached(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`, + async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getSiteRedirectBySource( + params.organizationId, + params.siteId, + { + shareKey: params.siteShareKey, + source: params.source, + } + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + } + return res.data; + }); + } + ); +}; + const getEmbedByUrl = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async (_, input: DataFetcherInput, params: { spaceId: string; url: string }) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'space', - space: params.spaceId, - }) + async (cacheKey, input: DataFetcherInput, params: { spaceId: string; url: string }) => { + if (GITBOOK_RUNTIME !== 'cloudflare') { + return getEmbedByUrlUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( + async () => { + return getEmbedByUrlUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.weeks, + tags: [], + } ); - return trace(`getEmbedByUrl(${params.spaceId}, ${params.url})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getEmbedByUrlInSpace( - params.spaceId, - { - url: params.url, - }, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('weeks'); - return res.data; - }); - }); + return uncached(); } ) ); +const getEmbedByUrlUseCache = async ( + input: DataFetcherInput, + params: { spaceId: string; url: string } +) => { + 'use cache'; + return getEmbedByUrlUncached(input, params, true); +}; + +const getEmbedByUrlUncached = async ( + input: DataFetcherInput, + params: { spaceId: string; url: string }, + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + getCacheTag({ + tag: 'space', + space: params.spaceId, + }) + ); + } + + return trace(`getEmbedByUrl.uncached(${params.spaceId}, ${params.url})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getEmbedByUrlInSpace(params.spaceId, { + url: params.url, + }); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('weeks'); + } + return res.data; + }); + }); +}; + const searchSiteContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: Parameters[0] ) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); + if (GITBOOK_RUNTIME !== 'cloudflare') { + return searchSiteContentUseCache(input, params); + } - return trace( - `searchSiteContent(${params.organizationId}, ${params.siteId}, ${params.query})`, + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( async () => { - return wrapDataFetcherError(async () => { - const { organizationId, siteId, query, scope } = params; - const api = apiClient(input); - const res = await api.orgs.searchSiteContent( - organizationId, - siteId, - { - query, - ...scope, - }, - {}, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('hours'); - return res.data.items; - }); + return searchSiteContentUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.hours, + tags: [], } ); + + return uncached(); } ) ); +const searchSiteContentUseCache = async ( + input: DataFetcherInput, + params: Parameters[0] +) => { + 'use cache'; + return searchSiteContentUncached(input, params, true); +}; + +const searchSiteContentUncached = async ( + input: DataFetcherInput, + params: Parameters[0], + withUseCache = false +) => { + if (withUseCache) { + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); + } + + return trace( + `searchSiteContent.uncached(${params.organizationId}, ${params.siteId}, ${params.query})`, + async () => { + return wrapDataFetcherError(async () => { + const { organizationId, siteId, query, scope } = params; + const api = apiClient(input); + const res = await api.orgs.searchSiteContent(organizationId, siteId, { + query, + ...scope, + }); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('hours'); + } + return res.data.items; + }); + } + ); +}; + const renderIntegrationUi = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - _, + cacheKey, input: DataFetcherInput, params: { integrationName: string; request: RenderIntegrationUI } ) => { - 'use cache'; - cacheTag( - getCacheTag({ - tag: 'integration', - integration: params.integrationName, - }) + if (GITBOOK_RUNTIME !== 'cloudflare') { + return renderIntegrationUiUseCache(input, params); + } + + // FIXME: OpenNext doesn't support 'use cache' yet + const uncached = unstable_cache( + async () => { + return renderIntegrationUiUncached(input, params); + }, + [cacheKey], + { + revalidate: RevalidationProfile.days, + tags: [ + getCacheTag({ + tag: 'integration', + integration: params.integrationName, + }), + ], + } ); - return trace(`renderIntegrationUi(${params.integrationName})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.integrations.renderIntegrationUiWithPost( - params.integrationName, - params.request, - { - ...noCacheFetchOptions, - } - ); - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - return res.data; - }); - }); + return uncached(); } ) ); +const renderIntegrationUiUseCache = async ( + input: DataFetcherInput, + params: { integrationName: string; request: RenderIntegrationUI } +) => { + 'use cache'; + return renderIntegrationUiUncached(input, params, true); +}; + +const renderIntegrationUiUncached = ( + input: DataFetcherInput, + params: { integrationName: string; request: RenderIntegrationUI }, + withUseCache = false +) => { + return trace(`renderIntegrationUi.uncached(${params.integrationName})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.integrations.renderIntegrationUiWithPost( + params.integrationName, + params.request + ); + if (withUseCache) { + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + } + return res.data; + }); + }); +}; + async function* streamAIResponse( input: DataFetcherInput, params: Parameters[0] ) { const api = apiClient(input); - const res = await api.orgs.streamAiResponseInSite( - params.organizationId, - params.siteId, - { - input: params.input, - output: params.output, - model: params.model, - }, - { - ...noCacheFetchOptions, - } - ); + const res = await api.orgs.streamAiResponseInSite(params.organizationId, params.siteId, { + input: params.input, + output: params.output, + model: params.model, + }); for await (const event of res) { yield event; From 8f9cb78ad04494d681eff6d0872ebd66fa84b344 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Fri, 9 May 2025 13:10:55 +0200 Subject: [PATCH 22/29] Revert "Rollback to unstable_cache to test" This reverts commit 68afdaa0c3eeba2c902f151590160556c66eeb87. --- packages/gitbook-v2/src/lib/data/api.ts | 1381 +++++++---------------- 1 file changed, 404 insertions(+), 977 deletions(-) diff --git a/packages/gitbook-v2/src/lib/data/api.ts b/packages/gitbook-v2/src/lib/data/api.ts index 20e495656e..7a2d03d16d 100644 --- a/packages/gitbook-v2/src/lib/data/api.ts +++ b/packages/gitbook-v2/src/lib/data/api.ts @@ -7,14 +7,8 @@ import { type RenderIntegrationUI, } from '@gitbook/api'; import { getCacheTag, getComputedContentSourceCacheTags } from '@gitbook/cache-tags'; -import { - GITBOOK_API_TOKEN, - GITBOOK_API_URL, - GITBOOK_RUNTIME, - 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_cache } from 'next/cache'; import { getCloudflareContext, getCloudflareRequestGlobal } from './cloudflare'; import { DataFetcherError, wrapDataFetcherError } from './errors'; import { withCacheKey, withoutConcurrentExecution } from './memoize'; @@ -28,16 +22,13 @@ interface DataFetcherInput { } /** - * Revalidation profile for the cache. - * Based on https://nextjs.org/docs/app/api-reference/functions/cacheLife#default-cache-profiles + * Options to pass to the `fetch` call to disable the Next data-cache when wrapped in `use cache`. */ -enum RevalidationProfile { - minutes = 60, - hours = 60 * 60, - days = 60 * 60 * 24, - weeks = 60 * 60 * 24 * 7, - max = 60 * 60 * 24 * 30, -} +export const noCacheFetchOptions: Partial = { + next: { + revalidate: 0, + }, +}; /** * Create a data fetcher using an API token. @@ -206,589 +197,301 @@ export function createDataFetcher( }; } -/* - * For the following functions, we: - * - Wrap them with `withCacheKey` to compute a cache key from the function arguments ONCE (to be performant) - * - Pass the cache key to `unstable_cache` to ensure the cache is not tied to closures - * - Call the uncached function in a `withoutConcurrentExecution` wrapper to prevent concurrent executions - * - * Important: - * - Only the function inside the `unstable_cache` is wrapped in `withoutConcurrentExecution` as Next.js needs to call - * the return of `unstable_cache` to identify the tags. - */ - const getUserById = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async (cacheKey, input: DataFetcherInput, params: { userId: string }) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getUserByIdUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getUserByIdUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [], - } - ); - - return uncached(); + async (_, input: DataFetcherInput, params: { userId: string }) => { + 'use cache'; + return trace(`getUserById(${params.userId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.users.getUserById(params.userId, { + ...noCacheFetchOptions, + }); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); + }); } ) ); -const getUserByIdUseCache = async (input: DataFetcherInput, params: { userId: string }) => { - 'use cache'; - return getUserByIdUncached(input, params, true); -}; - -const getUserByIdUncached = async ( - input: DataFetcherInput, - params: { userId: string }, - withUseCache = false -) => { - return trace(`getUserById.uncached(${params.userId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.users.getUserById(params.userId); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - }); -}; - const getSpace = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; shareKey: string | undefined } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getSpaceUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getSpaceUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'space', - space: params.spaceId, - }), - ], - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'space', + space: params.spaceId, + }) ); - return uncached(); + return trace(`getSpace(${params.spaceId}, ${params.shareKey})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getSpaceById( + params.spaceId, + { + shareKey: params.shareKey, + }, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); + }); } ) ); -const getSpaceUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; shareKey: string | undefined } -) => { - 'use cache'; - return getSpaceUncached(input, params, true); -}; - -const getSpaceUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; shareKey: string | undefined }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'space', - space: params.spaceId, - }) - ); - } - - return trace(`getSpace.uncached(${params.spaceId}, ${params.shareKey})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getSpaceById(params.spaceId, { - shareKey: params.shareKey, - }); - - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - }); -}; - const getChangeRequest = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; changeRequestId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getChangeRequestUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'change-request', + space: params.spaceId, + changeRequest: params.changeRequestId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getChangeRequest(${params.spaceId}, ${params.changeRequestId})`, async () => { - return getChangeRequestUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.minutes * 5, - tags: [ - getCacheTag({ - tag: 'change-request', - space: params.spaceId, - changeRequest: params.changeRequestId, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getChangeRequestById( + params.spaceId, + params.changeRequestId, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('minutes'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getChangeRequestUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; changeRequestId: string } -) => { - 'use cache'; - return getChangeRequestUncached(input, params, true); -}; - -const getChangeRequestUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; changeRequestId: string }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'change-request', - space: params.spaceId, - changeRequest: params.changeRequestId, - }) - ); - } - - return trace( - `getChangeRequest.uncached(${params.spaceId}, ${params.changeRequestId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getChangeRequestById( - params.spaceId, - params.changeRequestId - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('minutes'); - } - return res.data; - }); - } - ); -}; - const getRevision = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getRevisionUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getRevisionUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], - } - ); - - return uncached(); + 'use cache'; + return trace(`getRevision(${params.spaceId}, ${params.revisionId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getRevisionById( + params.spaceId, + params.revisionId, + { + metadata: params.metadata, + }, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); + }); } ) ); -const getRevisionUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean } -) => { - 'use cache'; - return getRevisionUncached(input, params, true); -}; - -const getRevisionUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean }, - withUseCache = false -) => { - return trace(`getRevision.uncached(${params.spaceId}, ${params.revisionId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getRevisionById(params.spaceId, params.revisionId, { - metadata: params.metadata, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - }); -}; - const getRevisionPages = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; metadata: boolean } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getRevisionPagesUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getRevisionPagesUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], - } - ); - - return uncached(); + 'use cache'; + return trace(`getRevisionPages(${params.spaceId}, ${params.revisionId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.listPagesInRevisionById( + params.spaceId, + params.revisionId, + { + metadata: params.metadata, + }, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data.pages; + }); + }); } ) ); -const getRevisionPagesUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean } -) => { - 'use cache'; - return getRevisionPagesUncached(input, params, true); -}; - -const getRevisionPagesUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; metadata: boolean }, - withUseCache = false -) => { - return trace(`getRevisionPages.uncached(${params.spaceId}, ${params.revisionId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.listPagesInRevisionById( - params.spaceId, - params.revisionId, - { - metadata: params.metadata, - } - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data.pages; - }); - }); -}; - const getRevisionFile = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; fileId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getRevisionFileUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getRevisionFile(${params.spaceId}, ${params.revisionId}, ${params.fileId})`, async () => { - return getRevisionFileUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getFileInRevisionById( + params.spaceId, + params.revisionId, + params.fileId, + {}, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getRevisionFileUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; fileId: string } -) => { - 'use cache'; - return getRevisionFileUncached(input, params, true); -}; - -const getRevisionFileUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; fileId: string }, - withUseCache = false -) => { - return trace( - `getRevisionFile.uncached(${params.spaceId}, ${params.revisionId}, ${params.fileId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getFileInRevisionById( - params.spaceId, - params.revisionId, - params.fileId, - {} - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getRevisionPageMarkdown = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; pageId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getRevisionPageMarkdownUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getRevisionPageMarkdown(${params.spaceId}, ${params.revisionId}, ${params.pageId})`, async () => { - return getRevisionPageMarkdownUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getPageInRevisionById( + params.spaceId, + params.revisionId, + params.pageId, + { + format: 'markdown', + }, + { + ...noCacheFetchOptions, + } + ); + + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + + if (!('markdown' in res.data)) { + throw new DataFetcherError('Page is not a document', 404); + } + return res.data.markdown; + }); } ); - - return uncached(); } ) ); -const getRevisionPageMarkdownUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; pageId: string } -) => { - 'use cache'; - return getRevisionPageMarkdownUncached(input, params, true); -}; - -const getRevisionPageMarkdownUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; pageId: string }, - withUseCache = false -) => { - return trace( - `getRevisionPageMarkdown.uncached(${params.spaceId}, ${params.revisionId}, ${params.pageId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getPageInRevisionById( - params.spaceId, - params.revisionId, - params.pageId, - { - format: 'markdown', - } - ); - - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - - if (!('markdown' in res.data)) { - throw new DataFetcherError('Page is not a document', 404); - } - return res.data.markdown; - }); - } - ); -}; - const getRevisionPageByPath = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; path: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getRevisionPageByPathUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getRevisionPageByPath(${params.spaceId}, ${params.revisionId}, ${params.path})`, async () => { - return getRevisionPageByPathUncached(input, params); - }, - [cacheKey, 'v2'], - { - revalidate: RevalidationProfile.max, - tags: [], + const encodedPath = encodeURIComponent(params.path); + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getPageInRevisionByPath( + params.spaceId, + params.revisionId, + encodedPath, + {}, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getRevisionPageByPathUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; path: string } -) => { - 'use cache'; - return getRevisionPageByPathUncached(input, params, true); -}; - -const getRevisionPageByPathUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; path: string }, - withUseCache = false -) => { - return trace( - `getRevisionPageByPath.uncached(${params.spaceId}, ${params.revisionId}, ${params.path})`, - async () => { - const encodedPath = encodeURIComponent(params.path); - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getPageInRevisionByPath( - params.spaceId, - params.revisionId, - encodedPath, - {} - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getDocument = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async ( - cacheKey, - input: DataFetcherInput, - params: { spaceId: string; documentId: string } - ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getDocumentUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getDocumentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], - } - ); - - return uncached(); + async (_, input: DataFetcherInput, params: { spaceId: string; documentId: string }) => { + 'use cache'; + return trace(`getDocument(${params.spaceId}, ${params.documentId})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getDocumentById( + params.spaceId, + params.documentId, + {}, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); + }); } ) ); -const getDocumentUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; documentId: string } -) => { - 'use cache'; - return getDocumentUncached(input, params, true); -}; - -const getDocumentUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; documentId: string }, - withUseCache = false -) => { - return trace(`getDocument.uncached(${params.spaceId}, ${params.documentId})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getDocumentById(params.spaceId, params.documentId, {}); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - }); -}; - const getComputedDocument = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; @@ -797,309 +500,157 @@ const getComputedDocument = withCacheKey( seed: string; } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getComputedDocumentUseCache(input, params); - } + 'use cache'; + cacheTag( + ...getComputedContentSourceCacheTags( + { + spaceId: params.spaceId, + organizationId: params.organizationId, + }, + params.source + ) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getComputedDocument(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`, async () => { - return getComputedDocumentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: getComputedContentSourceCacheTags( - { - spaceId: params.spaceId, - organizationId: params.organizationId, - }, - params.source - ), + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getComputedDocument( + params.spaceId, + { + source: params.source, + seed: params.seed, + }, + {}, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getComputedDocumentUseCache = async ( - input: DataFetcherInput, - params: { - spaceId: string; - organizationId: string; - source: ComputedContentSource; - seed: string; - } -) => { - 'use cache'; - return getComputedDocumentUncached(input, params, true); -}; - -const getComputedDocumentUncached = async ( - input: DataFetcherInput, - params: { - spaceId: string; - organizationId: string; - source: ComputedContentSource; - seed: string; - }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - ...getComputedContentSourceCacheTags( - { - spaceId: params.spaceId, - organizationId: params.organizationId, - }, - params.source - ) - ); - } - - return trace( - `getComputedDocument.uncached(${params.spaceId}, ${params.organizationId}, ${params.source.type}, ${params.seed})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getComputedDocument(params.spaceId, { - source: params.source, - seed: params.seed, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getReusableContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { spaceId: string; revisionId: string; reusableContentId: string } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getReusableContentUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + 'use cache'; + return trace( + `getReusableContent(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`, async () => { - return getReusableContentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getReusableContentInRevisionById( + params.spaceId, + params.revisionId, + params.reusableContentId, + {}, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getReusableContentUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; reusableContentId: string } -) => { - 'use cache'; - return getReusableContentUncached(input, params, true); -}; - -const getReusableContentUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; revisionId: string; reusableContentId: string }, - withUseCache = false -) => { - return trace( - `getReusableContent.uncached(${params.spaceId}, ${params.revisionId}, ${params.reusableContentId})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getReusableContentInRevisionById( - params.spaceId, - params.revisionId, - params.reusableContentId - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getLatestOpenAPISpecVersionContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async ( - cacheKey, - input: DataFetcherInput, - params: { organizationId: string; slug: string } - ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getLatestOpenAPISpecVersionContentUseCache(input, params); - } + async (_, input: DataFetcherInput, params: { organizationId: string; slug: string }) => { + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'openapi', + organization: params.organizationId, + openAPISpec: params.slug, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getLatestOpenAPISpecVersionContent(${params.organizationId}, ${params.slug})`, async () => { - return getLatestOpenAPISpecVersionContentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.max, - tags: [ - getCacheTag({ - tag: 'openapi', - organization: params.organizationId, - openAPISpec: params.slug, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getLatestOpenApiSpecVersionContent( + params.organizationId, + params.slug, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('max'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getLatestOpenAPISpecVersionContentUseCache = async ( - input: DataFetcherInput, - params: { organizationId: string; slug: string } -) => { - 'use cache'; - return getLatestOpenAPISpecVersionContentUncached(input, params, true); -}; - -const getLatestOpenAPISpecVersionContentUncached = async ( - input: DataFetcherInput, - params: { organizationId: string; slug: string }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'openapi', - organization: params.organizationId, - openAPISpec: params.slug, - }) - ); - } - - return trace( - `getLatestOpenAPISpecVersionContent.uncached(${params.organizationId}, ${params.slug})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getLatestOpenApiSpecVersionContent( - params.organizationId, - params.slug - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('max'); - } - return res.data; - }); - } - ); -}; - const getPublishedContentSite = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { organizationId: string; siteId: string; siteShareKey: string | undefined } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getPublishedContentSiteUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getPublishedContentSite(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`, async () => { - return getPublishedContentSiteUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'site', - site: params.siteId, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getPublishedContentSite( + params.organizationId, + params.siteId, + { + shareKey: params.siteShareKey, + }, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getPublishedContentSiteUseCache = async ( - input: DataFetcherInput, - params: { organizationId: string; siteId: string; siteShareKey: string | undefined } -) => { - 'use cache'; - return getPublishedContentSiteUncached(input, params, true); -}; - -const getPublishedContentSiteUncached = async ( - input: DataFetcherInput, - params: { organizationId: string; siteId: string; siteShareKey: string | undefined }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); - } - - return trace( - `getPublishedContentSite.uncached(${params.organizationId}, ${params.siteId}, ${params.siteShareKey})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getPublishedContentSite( - params.organizationId, - params.siteId, - { - shareKey: params.siteShareKey, - } - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - } - ); -}; - const getSiteRedirectBySource = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { organizationId: string; @@ -1108,293 +659,169 @@ const getSiteRedirectBySource = withCacheKey( source: string; } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getSiteRedirectBySourceUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `getSiteRedirectBySource(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`, async () => { - return getSiteRedirectBySourceUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'site', - site: params.siteId, - }), - ], + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.orgs.getSiteRedirectBySource( + params.organizationId, + params.siteId, + { + shareKey: params.siteShareKey, + source: params.source, + }, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); } ); - - return uncached(); } ) ); -const getSiteRedirectBySourceUseCache = async ( - input: DataFetcherInput, - params: { - organizationId: string; - siteId: string; - siteShareKey: string | undefined; - source: string; - } -) => { - 'use cache'; - return getSiteRedirectBySourceUncached(input, params, true); -}; - -const getSiteRedirectBySourceUncached = async ( - input: DataFetcherInput, - params: { - organizationId: string; - siteId: string; - siteShareKey: string | undefined; - source: string; - }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); - } - - return trace( - `getSiteRedirectBySource.uncached(${params.organizationId}, ${params.siteId}, ${params.siteShareKey}, ${params.source})`, - async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.orgs.getSiteRedirectBySource( - params.organizationId, - params.siteId, - { - shareKey: params.siteShareKey, - source: params.source, - } - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - } - ); -}; - const getEmbedByUrl = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, - async (cacheKey, input: DataFetcherInput, params: { spaceId: string; url: string }) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return getEmbedByUrlUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return getEmbedByUrlUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.weeks, - tags: [], - } + async (_, input: DataFetcherInput, params: { spaceId: string; url: string }) => { + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'space', + space: params.spaceId, + }) ); - return uncached(); + return trace(`getEmbedByUrl(${params.spaceId}, ${params.url})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.spaces.getEmbedByUrlInSpace( + params.spaceId, + { + url: params.url, + }, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('weeks'); + return res.data; + }); + }); } ) ); -const getEmbedByUrlUseCache = async ( - input: DataFetcherInput, - params: { spaceId: string; url: string } -) => { - 'use cache'; - return getEmbedByUrlUncached(input, params, true); -}; - -const getEmbedByUrlUncached = async ( - input: DataFetcherInput, - params: { spaceId: string; url: string }, - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'space', - space: params.spaceId, - }) - ); - } - - return trace(`getEmbedByUrl.uncached(${params.spaceId}, ${params.url})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.spaces.getEmbedByUrlInSpace(params.spaceId, { - url: params.url, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('weeks'); - } - return res.data; - }); - }); -}; - const searchSiteContent = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: Parameters[0] ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return searchSiteContentUseCache(input, params); - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'site', + site: params.siteId, + }) + ); - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( + return trace( + `searchSiteContent(${params.organizationId}, ${params.siteId}, ${params.query})`, async () => { - return searchSiteContentUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.hours, - tags: [], + return wrapDataFetcherError(async () => { + const { organizationId, siteId, query, scope } = params; + const api = apiClient(input); + const res = await api.orgs.searchSiteContent( + organizationId, + siteId, + { + query, + ...scope, + }, + {}, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('hours'); + return res.data.items; + }); } ); - - return uncached(); } ) ); -const searchSiteContentUseCache = async ( - input: DataFetcherInput, - params: Parameters[0] -) => { - 'use cache'; - return searchSiteContentUncached(input, params, true); -}; - -const searchSiteContentUncached = async ( - input: DataFetcherInput, - params: Parameters[0], - withUseCache = false -) => { - if (withUseCache) { - cacheTag( - getCacheTag({ - tag: 'site', - site: params.siteId, - }) - ); - } - - return trace( - `searchSiteContent.uncached(${params.organizationId}, ${params.siteId}, ${params.query})`, - async () => { - return wrapDataFetcherError(async () => { - const { organizationId, siteId, query, scope } = params; - const api = apiClient(input); - const res = await api.orgs.searchSiteContent(organizationId, siteId, { - query, - ...scope, - }); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('hours'); - } - return res.data.items; - }); - } - ); -}; - const renderIntegrationUi = withCacheKey( withoutConcurrentExecution( getCloudflareRequestGlobal, async ( - cacheKey, + _, input: DataFetcherInput, params: { integrationName: string; request: RenderIntegrationUI } ) => { - if (GITBOOK_RUNTIME !== 'cloudflare') { - return renderIntegrationUiUseCache(input, params); - } - - // FIXME: OpenNext doesn't support 'use cache' yet - const uncached = unstable_cache( - async () => { - return renderIntegrationUiUncached(input, params); - }, - [cacheKey], - { - revalidate: RevalidationProfile.days, - tags: [ - getCacheTag({ - tag: 'integration', - integration: params.integrationName, - }), - ], - } + 'use cache'; + cacheTag( + getCacheTag({ + tag: 'integration', + integration: params.integrationName, + }) ); - return uncached(); + return trace(`renderIntegrationUi(${params.integrationName})`, async () => { + return wrapDataFetcherError(async () => { + const api = apiClient(input); + const res = await api.integrations.renderIntegrationUiWithPost( + params.integrationName, + params.request, + { + ...noCacheFetchOptions, + } + ); + cacheTag(...getCacheTagsFromResponse(res)); + cacheLife('days'); + return res.data; + }); + }); } ) ); -const renderIntegrationUiUseCache = async ( - input: DataFetcherInput, - params: { integrationName: string; request: RenderIntegrationUI } -) => { - 'use cache'; - return renderIntegrationUiUncached(input, params, true); -}; - -const renderIntegrationUiUncached = ( - input: DataFetcherInput, - params: { integrationName: string; request: RenderIntegrationUI }, - withUseCache = false -) => { - return trace(`renderIntegrationUi.uncached(${params.integrationName})`, async () => { - return wrapDataFetcherError(async () => { - const api = apiClient(input); - const res = await api.integrations.renderIntegrationUiWithPost( - params.integrationName, - params.request - ); - if (withUseCache) { - cacheTag(...getCacheTagsFromResponse(res)); - cacheLife('days'); - } - return res.data; - }); - }); -}; - async function* streamAIResponse( input: DataFetcherInput, params: Parameters[0] ) { const api = apiClient(input); - const res = await api.orgs.streamAiResponseInSite(params.organizationId, params.siteId, { - input: params.input, - output: params.output, - model: params.model, - }); + const res = await api.orgs.streamAiResponseInSite( + params.organizationId, + params.siteId, + { + input: params.input, + output: params.output, + model: params.model, + }, + { + ...noCacheFetchOptions, + } + ); for await (const event of res) { yield event; From 5a7f884636b273be1f5bc3ff97a950a57811fc8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Wed, 14 May 2025 10:18:24 +0200 Subject: [PATCH 23/29] bun install --- bun.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 46ac2592dd..7beffc67ad 100644 --- a/bun.lock +++ b/bun.lock @@ -156,7 +156,7 @@ "warn-once": "^0.1.1", }, "devDependencies": { - "@opennextjs/cloudflare": "^1.0.0-beta.3", + "@opennextjs/cloudflare": "^1.0.1", "@types/rison": "^0.0.9", "gitbook": "*", "postcss": "^8", @@ -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=="], - "@opennextjs/aws": ["@opennextjs/aws@3.5.7", "", { "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-YjyHJrkIHI7YwQRCp8GjDOudu86oOc1RiwxvBBpPHrplsS18H4ZmkzGggAKhK6B4myGsJQ/q9kNP2TraoZiNzg=="], + "@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/cloudflare": ["@opennextjs/cloudflare@1.0.0-beta.3", "", { "dependencies": { "@dotenvx/dotenvx": "1.31.0", "@opennextjs/aws": "3.5.7", "enquirer": "^2.4.1", "glob": "^11.0.0", "ts-tqdm": "^0.8.6" }, "peerDependencies": { "wrangler": "^3.114.3 || ^4.7.0" }, "bin": { "opennextjs-cloudflare": "dist/cli/index.js" } }, "sha512-qKBXQZhUeQ+iGvfJeF7PO30g59LHnPOlRVZd77zxwn6Uc9C+c0LSwo8N28XRIWyQPkY007rKk9pSIxOrP4MHtQ=="], + "@opennextjs/cloudflare": ["@opennextjs/cloudflare@1.0.2", "", { "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-K/mk2Auz5ZKAATkBv6XcqNpjcZkyZ7mOpAqgmHPsihO9KWN3/+DS18XISm2jyAlX5ksl4aVbYv6OFt1rQ2UJPA=="], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], From d630151d13303048bb27f0ddd9feb1b57ae361e0 Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Wed, 14 May 2025 18:49:57 +0200 Subject: [PATCH 24/29] patch next als snapshot --- bun.lock | 3 +-- package.json | 3 ++- patches/next@15.3.2.patch | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 patches/next@15.3.2.patch diff --git a/bun.lock b/bun.lock index 7beffc67ad..3ad9d6cdaf 100644 --- a/bun.lock +++ b/bun.lock @@ -257,6 +257,7 @@ "patchedDependencies": { "decode-named-character-reference@1.0.2": "patches/decode-named-character-reference@1.0.2.patch", "@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch", + "next@15.3.2": "patches/next@15.3.2.patch", }, "overrides": { "@codemirror/state": "6.4.1", @@ -635,8 +636,6 @@ "@gitbook/emoji-codepoints": ["@gitbook/emoji-codepoints@workspace:packages/emoji-codepoints"], - "@gitbook/fontawesome-pro": ["@gitbook/fontawesome-pro@1.0.8", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "^6.6.0" } }, "sha512-i4PgiuGyUb52Muhc52kK3aMJIMfMkA2RbPW30tre8a6M8T6mWTfYo6gafSgjNvF1vH29zcuB8oBYnF0gO4XcHA=="], - "@gitbook/icons": ["@gitbook/icons@workspace:packages/icons"], "@gitbook/openapi-parser": ["@gitbook/openapi-parser@workspace:packages/openapi-parser"], diff --git a/package.json b/package.json index 5398e52f9f..f05b13c3c1 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "workspaces": ["packages/*"], "patchedDependencies": { "decode-named-character-reference@1.0.2": "patches/decode-named-character-reference@1.0.2.patch", - "@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch" + "@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch", + "next@15.3.2": "patches/next@15.3.2.patch" } } diff --git a/patches/next@15.3.2.patch b/patches/next@15.3.2.patch new file mode 100644 index 0000000000..b654ca3ea4 --- /dev/null +++ b/patches/next@15.3.2.patch @@ -0,0 +1,17 @@ +diff --git a/dist/server/app-render/async-local-storage.js b/dist/server/app-render/async-local-storage.js +index 5b3b462376c2d0fb7982f06e6d7256ff8ab796f3..5b74509eac151571f0b73dcb86a7d87d08b1a9f5 100644 +--- a/dist/server/app-render/async-local-storage.js ++++ b/dist/server/app-render/async-local-storage.js +@@ -64,9 +64,9 @@ function bindSnapshot(fn) { + return FakeAsyncLocalStorage.bind(fn); + } + function createSnapshot() { +- if (maybeGlobalAsyncLocalStorage) { +- return maybeGlobalAsyncLocalStorage.snapshot(); +- } ++ // if (maybeGlobalAsyncLocalStorage) { ++ // return maybeGlobalAsyncLocalStorage.snapshot(); ++ // } + return function(fn, ...args) { + return fn(...args); + }; From a43968b688c0bfba4f575b0845cc379795a13dca Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Thu, 15 May 2025 17:29:33 +0200 Subject: [PATCH 25/29] Revert "patch next als snapshot" This reverts commit d630151d13303048bb27f0ddd9feb1b57ae361e0. --- bun.lock | 3 ++- package.json | 3 +-- patches/next@15.3.2.patch | 17 ----------------- 3 files changed, 3 insertions(+), 20 deletions(-) delete mode 100644 patches/next@15.3.2.patch diff --git a/bun.lock b/bun.lock index 3ad9d6cdaf..7beffc67ad 100644 --- a/bun.lock +++ b/bun.lock @@ -257,7 +257,6 @@ "patchedDependencies": { "decode-named-character-reference@1.0.2": "patches/decode-named-character-reference@1.0.2.patch", "@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch", - "next@15.3.2": "patches/next@15.3.2.patch", }, "overrides": { "@codemirror/state": "6.4.1", @@ -636,6 +635,8 @@ "@gitbook/emoji-codepoints": ["@gitbook/emoji-codepoints@workspace:packages/emoji-codepoints"], + "@gitbook/fontawesome-pro": ["@gitbook/fontawesome-pro@1.0.8", "", { "dependencies": { "@fortawesome/fontawesome-common-types": "^6.6.0" } }, "sha512-i4PgiuGyUb52Muhc52kK3aMJIMfMkA2RbPW30tre8a6M8T6mWTfYo6gafSgjNvF1vH29zcuB8oBYnF0gO4XcHA=="], + "@gitbook/icons": ["@gitbook/icons@workspace:packages/icons"], "@gitbook/openapi-parser": ["@gitbook/openapi-parser@workspace:packages/openapi-parser"], diff --git a/package.json b/package.json index f05b13c3c1..5398e52f9f 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,6 @@ "workspaces": ["packages/*"], "patchedDependencies": { "decode-named-character-reference@1.0.2": "patches/decode-named-character-reference@1.0.2.patch", - "@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch", - "next@15.3.2": "patches/next@15.3.2.patch" + "@vercel/next@4.4.2": "patches/@vercel%2Fnext@4.4.2.patch" } } diff --git a/patches/next@15.3.2.patch b/patches/next@15.3.2.patch deleted file mode 100644 index b654ca3ea4..0000000000 --- a/patches/next@15.3.2.patch +++ /dev/null @@ -1,17 +0,0 @@ -diff --git a/dist/server/app-render/async-local-storage.js b/dist/server/app-render/async-local-storage.js -index 5b3b462376c2d0fb7982f06e6d7256ff8ab796f3..5b74509eac151571f0b73dcb86a7d87d08b1a9f5 100644 ---- a/dist/server/app-render/async-local-storage.js -+++ b/dist/server/app-render/async-local-storage.js -@@ -64,9 +64,9 @@ function bindSnapshot(fn) { - return FakeAsyncLocalStorage.bind(fn); - } - function createSnapshot() { -- if (maybeGlobalAsyncLocalStorage) { -- return maybeGlobalAsyncLocalStorage.snapshot(); -- } -+ // if (maybeGlobalAsyncLocalStorage) { -+ // return maybeGlobalAsyncLocalStorage.snapshot(); -+ // } - return function(fn, ...args) { - return fn(...args); - }; From 1290f95a31413d4703e8346005f3d244fc89b192 Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Thu, 15 May 2025 17:40:48 +0200 Subject: [PATCH 26/29] use fix in OpenNext --- bun.lock | 3 +++ packages/gitbook-v2/package.json | 1 + 2 files changed, 4 insertions(+) diff --git a/bun.lock b/bun.lock index 7beffc67ad..c5ae4dafb8 100644 --- a/bun.lock +++ b/bun.lock @@ -147,6 +147,7 @@ "@gitbook/cache-tags": "workspace:*", "@sindresorhus/fnv1a": "^3.1.0", "assert-never": "^1.2.1", + "@opennextjs/cloudflare": "https://pkg.pr.new/@opennextjs/cloudflare@666", "jwt-decode": "^4.0.0", "next": "^15.3.2", "react": "^19.0.0", @@ -4077,6 +4078,8 @@ "gaxios/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "gitbook-v2/@opennextjs/cloudflare": ["@opennextjs/cloudflare@https://pkg.pr.new/@opennextjs/cloudflare@666", { "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" } }], + "gitbook-v2/next": ["next@15.3.2", "", { "dependencies": { "@next/env": "15.3.2", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.2", "@next/swc-darwin-x64": "15.3.2", "@next/swc-linux-arm64-gnu": "15.3.2", "@next/swc-linux-arm64-musl": "15.3.2", "@next/swc-linux-x64-gnu": "15.3.2", "@next/swc-linux-x64-musl": "15.3.2", "@next/swc-win32-arm64-msvc": "15.3.2", "@next/swc-win32-x64-msvc": "15.3.2", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ=="], "global-dirs/ini": ["ini@1.3.7", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="], diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json index 9701a2a332..a4e9264952 100644 --- a/packages/gitbook-v2/package.json +++ b/packages/gitbook-v2/package.json @@ -5,6 +5,7 @@ "dependencies": { "@gitbook/api": "^0.115.0", "@gitbook/cache-tags": "workspace:*", + "@opennextjs/cloudflare": "https://pkg.pr.new/@opennextjs/cloudflare@666", "@sindresorhus/fnv1a": "^3.1.0", "assert-never": "^1.2.1", "jwt-decode": "^4.0.0", From 4b71a4e99b02a95f1e84e6e30098f28e815a2221 Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Thu, 15 May 2025 17:45:41 +0200 Subject: [PATCH 27/29] fix lockfile --- bun.lock | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/bun.lock b/bun.lock index c5ae4dafb8..206bf6057d 100644 --- a/bun.lock +++ b/bun.lock @@ -145,9 +145,9 @@ "dependencies": { "@gitbook/api": "^0.115.0", "@gitbook/cache-tags": "workspace:*", + "@opennextjs/cloudflare": "https://pkg.pr.new/@opennextjs/cloudflare@666", "@sindresorhus/fnv1a": "^3.1.0", "assert-never": "^1.2.1", - "@opennextjs/cloudflare": "https://pkg.pr.new/@opennextjs/cloudflare@666", "jwt-decode": "^4.0.0", "next": "^15.3.2", "react": "^19.0.0", @@ -794,7 +794,7 @@ "@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/cloudflare": ["@opennextjs/cloudflare@1.0.2", "", { "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-K/mk2Auz5ZKAATkBv6XcqNpjcZkyZ7mOpAqgmHPsihO9KWN3/+DS18XISm2jyAlX5ksl4aVbYv6OFt1rQ2UJPA=="], + "@opennextjs/cloudflare": ["@opennextjs/cloudflare@https://pkg.pr.new/@opennextjs/cloudflare@666", { "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" } }], "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], @@ -4078,8 +4078,6 @@ "gaxios/node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], - "gitbook-v2/@opennextjs/cloudflare": ["@opennextjs/cloudflare@https://pkg.pr.new/@opennextjs/cloudflare@666", { "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" } }], - "gitbook-v2/next": ["next@15.3.2", "", { "dependencies": { "@next/env": "15.3.2", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "15.3.2", "@next/swc-darwin-x64": "15.3.2", "@next/swc-linux-arm64-gnu": "15.3.2", "@next/swc-linux-arm64-musl": "15.3.2", "@next/swc-linux-x64-gnu": "15.3.2", "@next/swc-linux-x64-musl": "15.3.2", "@next/swc-win32-arm64-msvc": "15.3.2", "@next/swc-win32-x64-msvc": "15.3.2", "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.41.2", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ=="], "global-dirs/ini": ["ini@1.3.7", "", {}, "sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ=="], From 54c1d93cb44ffa704e4359ffd49c33710049af2c Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Fri, 16 May 2025 14:40:48 +0200 Subject: [PATCH 28/29] remove dev deps --- bun.lock | 1 - packages/gitbook-v2/package.json | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/bun.lock b/bun.lock index 206bf6057d..a7d865c06d 100644 --- a/bun.lock +++ b/bun.lock @@ -157,7 +157,6 @@ "warn-once": "^0.1.1", }, "devDependencies": { - "@opennextjs/cloudflare": "^1.0.1", "@types/rison": "^0.0.9", "gitbook": "*", "postcss": "^8", diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json index a4e9264952..7d9be97ecd 100644 --- a/packages/gitbook-v2/package.json +++ b/packages/gitbook-v2/package.json @@ -18,7 +18,6 @@ }, "devDependencies": { "gitbook": "*", - "@opennextjs/cloudflare": "^1.0.1", "@types/rison": "^0.0.9", "tailwindcss": "^3.4.0", "postcss": "^8" @@ -34,4 +33,4 @@ "unit": "bun test", "typecheck": "tsc --noEmit" } -} +} \ No newline at end of file From c9df34e9bdb0f872766763b2533127b29bc978c3 Mon Sep 17 00:00:00 2001 From: Nicolas Dorseuil Date: Fri, 16 May 2025 14:42:11 +0200 Subject: [PATCH 29/29] format --- packages/gitbook-v2/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/gitbook-v2/package.json b/packages/gitbook-v2/package.json index 7d9be97ecd..646bfb4719 100644 --- a/packages/gitbook-v2/package.json +++ b/packages/gitbook-v2/package.json @@ -33,4 +33,4 @@ "unit": "bun test", "typecheck": "tsc --noEmit" } -} \ No newline at end of file +}