Skip to content

Commit 3193611

Browse files
committed
test local implementation
1 parent 07eab98 commit 3193611

File tree

15 files changed

+722
-17
lines changed

15 files changed

+722
-17
lines changed

bun.lock

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@
164164
"name": "gitbook-v2",
165165
"version": "0.3.0",
166166
"dependencies": {
167+
"@cloudflare/containers": "^0.0.8",
168+
"@cloudflare/workers-types": "^4.20250620.0",
167169
"@gitbook/api": "catalog:",
168170
"@gitbook/cache-tags": "workspace:*",
169171
"@opennextjs/cloudflare": "1.2.1",
@@ -279,6 +281,7 @@
279281
"patchedDependencies": {
280282
281283
"@vercel/[email protected]": "patches/@vercel%[email protected]",
284+
"@opennextjs/[email protected]": "patches/@opennextjs%[email protected]",
282285
},
283286
"overrides": {
284287
"@codemirror/state": "6.4.1",
@@ -495,6 +498,8 @@
495498

496499
"@changesets/write": ["@changesets/[email protected]", "", { "dependencies": { "@changesets/types": "^6.0.0", "fs-extra": "^7.0.1", "human-id": "^1.0.2", "prettier": "^2.7.1" } }, "sha512-kDxDrPNpUgsjDbWBvUo27PzKX4gqeKOlhibaOXDJA6kuBisGqNHv/HwGJrAu8U/dSf8ZEFIeHIPtvSlZI1kULw=="],
497500

501+
"@cloudflare/containers": ["@cloudflare/[email protected]", "", {}, "sha512-Uaw11AaUN0jVxRAd6PW33shAA4D4u0Yei3Xl/AOSijguYDhDGDMW4Dykx8vYUAl4tCbV9hGxvBgw9mAFCFXoJw=="],
502+
498503
"@cloudflare/kv-asset-handler": ["@cloudflare/[email protected]", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="],
499504

500505
"@cloudflare/next-on-pages": ["@cloudflare/[email protected]", "", { "dependencies": { "acorn": "^8.8.0", "ast-types": "^0.14.2", "chalk": "^5.2.0", "chokidar": "^3.5.3", "commander": "^11.1.0", "cookie": "^0.5.0", "esbuild": "^0.15.3", "js-yaml": "^4.1.0", "miniflare": "^3.20231218.1", "package-manager-manager": "^0.2.0", "pcre-to-regexp": "^1.1.0", "semver": "^7.5.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20240208.0", "vercel": ">=30.0.0", "wrangler": "^3.28.2 || ^4.0.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "next-on-pages": "bin/index.js" } }, "sha512-rPy7x9c2+0RDDdJ5o0TeRUwXJ1b7N1epnqF6qKSp5Wz1r9KHOyvaZh1ACoOC6Vu5k9su5WZOgy+8fPLIyrldMQ=="],
@@ -511,7 +516,7 @@
511516

512517
"@cloudflare/workerd-windows-64": ["@cloudflare/[email protected]", "", { "os": "win32", "cpu": "x64" }, "sha512-dK9I8zBX5rR7MtaaP2AhICQTEw3PVzHcsltN8o46w7JsbYlMvFOj27FfYH5dhs3IahgmIfw2e572QXW2o/dbpg=="],
513518

514-
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20241230.0", "", {}, "sha512-dtLD4jY35Lb750cCVyO1i/eIfdZJg2Z0i+B1RYX6BVeRPlgaHx/H18ImKAkYmy0g09Ow8R2jZy3hIxMgXun0WQ=="],
519+
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250620.0", "", {}, "sha512-EVvRB/DJEm6jhdKg+A4Qm4y/ry1cIvylSgSO3/f/Bv161vldDRxaXM2YoQQWFhLOJOw0qtrHsKOD51KYxV1XCw=="],
515520

516521
"@codemirror/autocomplete": ["@codemirror/[email protected]", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA=="],
517522

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
},
4242
"patchedDependencies": {
4343
44-
"@vercel/[email protected]": "patches/@vercel%[email protected]"
44+
"@vercel/[email protected]": "patches/@vercel%[email protected]",
45+
"@opennextjs/[email protected]": "patches/@opennextjs%[email protected]"
4546
}
4647
}

packages/gitbook-v2/open-next.config.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import type { OpenNextConfig } from '@opennextjs/cloudflare';
33
export default {
44
default: {
55
override: {
6-
wrapper: 'cloudflare-node',
7-
converter: 'edge',
8-
proxyExternalRequest: 'fetch',
9-
queue: () => import('./openNext/queue/middleware').then((m) => m.default),
10-
incrementalCache: () => import('./openNext/incrementalCache').then((m) => m.default),
11-
tagCache: () => import('./openNext/tagCache/middleware').then((m) => m.default),
6+
wrapper: 'node',
7+
converter: 'node',
8+
proxyExternalRequest: 'node',
9+
generateDockerfile: true,
10+
queue: 'dummy',
11+
incrementalCache: () => import('./openNext/noOpCache').then((m) => m.default),
12+
tagCache: 'dummy',
1213
},
1314
},
1415
middleware: {
@@ -26,4 +27,7 @@ export default {
2627
enableCacheInterception: true,
2728
},
2829
edgeExternals: ['node:crypto'],
30+
cloudflare: {
31+
dangerousDisableConfigValidation: true,
32+
},
2933
} satisfies OpenNextConfig;
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
globalThis.openNextDebug = false;globalThis.openNextVersion = "3.6.5";
2+
3+
// ../../node_modules/@opennextjs/cloudflare/dist/api/durable-objects/bucket-cache-purge.js
4+
import { DurableObject } from "cloudflare:workers";
5+
6+
// ../../node_modules/@opennextjs/cloudflare/dist/api/cloudflare-context.js
7+
var cloudflareContextSymbol = Symbol.for("__cloudflare-context__");
8+
9+
// ../../node_modules/@opennextjs/cloudflare/dist/api/overrides/internal.js
10+
var debugCache = (name, ...args) => {
11+
if (process.env.NEXT_PRIVATE_DEBUG_CACHE) {
12+
console.log(`[${name}] `, ...args);
13+
}
14+
};
15+
async function internalPurgeCacheByTags(env, tags) {
16+
if (!env.CACHE_PURGE_ZONE_ID && !env.CACHE_PURGE_API_TOKEN) {
17+
debugCache("purgeCacheByTags", "No cache zone ID or API token provided. Skipping cache purge.");
18+
return "missing-credentials";
19+
}
20+
try {
21+
const response = await fetch(`https://api.cloudflare.com/client/v4/zones/${env.CACHE_PURGE_ZONE_ID}/purge_cache`, {
22+
headers: {
23+
Authorization: `Bearer ${env.CACHE_PURGE_API_TOKEN}`,
24+
"Content-Type": "application/json"
25+
},
26+
method: "POST",
27+
body: JSON.stringify({
28+
tags
29+
})
30+
});
31+
if (response.status === 429) {
32+
debugCache("purgeCacheByTags", "Rate limit exceeded. Skipping cache purge.");
33+
return "rate-limit-exceeded";
34+
}
35+
const bodyResponse = await response.json();
36+
if (!bodyResponse.success) {
37+
debugCache("purgeCacheByTags", "Cache purge failed. Errors:", bodyResponse.errors.map((error) => `${error.code}: ${error.message}`));
38+
return "purge-failed";
39+
}
40+
debugCache("purgeCacheByTags", "Cache purged successfully for tags:", tags);
41+
return "purge-success";
42+
} catch (error) {
43+
console.error("Error purging cache by tags:", error);
44+
return "purge-failed";
45+
}
46+
}
47+
48+
// ../../node_modules/@opennextjs/cloudflare/dist/api/durable-objects/bucket-cache-purge.js
49+
var DEFAULT_BUFFER_TIME_IN_SECONDS = 5;
50+
var MAX_NUMBER_OF_TAGS_PER_PURGE = 100;
51+
var BucketCachePurge = class extends DurableObject {
52+
bufferTimeInSeconds;
53+
constructor(state, env) {
54+
super(state, env);
55+
this.bufferTimeInSeconds = env.NEXT_CACHE_DO_PURGE_BUFFER_TIME_IN_SECONDS ? parseInt(env.NEXT_CACHE_DO_PURGE_BUFFER_TIME_IN_SECONDS) : DEFAULT_BUFFER_TIME_IN_SECONDS;
56+
state.blockConcurrencyWhile(async () => {
57+
state.storage.sql.exec(`
58+
CREATE TABLE IF NOT EXISTS cache_purge (
59+
tag TEXT NOT NULL
60+
);
61+
CREATE UNIQUE INDEX IF NOT EXISTS tag_index ON cache_purge (tag);
62+
`);
63+
});
64+
}
65+
async purgeCacheByTags(tags) {
66+
for (const tag of tags) {
67+
this.ctx.storage.sql.exec(`
68+
INSERT OR REPLACE INTO cache_purge (tag)
69+
VALUES (?)`, [tag]);
70+
}
71+
const nextAlarm = await this.ctx.storage.getAlarm();
72+
if (!nextAlarm) {
73+
this.ctx.storage.setAlarm(Date.now() + this.bufferTimeInSeconds * 1e3);
74+
}
75+
}
76+
async alarm() {
77+
let tags = this.ctx.storage.sql.exec(`
78+
SELECT * FROM cache_purge LIMIT ${MAX_NUMBER_OF_TAGS_PER_PURGE}
79+
`).toArray();
80+
do {
81+
if (tags.length === 0) {
82+
return;
83+
}
84+
const result = await internalPurgeCacheByTags(this.env, tags.map((row) => row.tag));
85+
if (result === "rate-limit-exceeded") {
86+
throw new Error("Rate limit exceeded");
87+
}
88+
this.ctx.storage.sql.exec(`
89+
DELETE FROM cache_purge
90+
WHERE tag IN (${tags.map(() => "?").join(",")})
91+
`, tags.map((row) => row.tag));
92+
if (tags.length < MAX_NUMBER_OF_TAGS_PER_PURGE) {
93+
tags = [];
94+
} else {
95+
tags = this.ctx.storage.sql.exec(`
96+
SELECT * FROM cache_purge LIMIT ${MAX_NUMBER_OF_TAGS_PER_PURGE}
97+
`).toArray();
98+
}
99+
} while (tags.length >= 0);
100+
}
101+
};
102+
export {
103+
BucketCachePurge
104+
};

0 commit comments

Comments
 (0)