diff --git a/apps/docs-proxy/package.json b/apps/docs-proxy/package.json new file mode 100644 index 000000000..a8df4af9b --- /dev/null +++ b/apps/docs-proxy/package.json @@ -0,0 +1,15 @@ +{ + "name": "@repo/docs-proxy", + "version": "1.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "wrangler dev", + "deploy": "wrangler deploy --minify" + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "typescript": "^5.8.3", + "wrangler": "^4.4.0" + } +} diff --git a/apps/docs-proxy/src/index.ts b/apps/docs-proxy/src/index.ts new file mode 100644 index 000000000..6b910bc1c --- /dev/null +++ b/apps/docs-proxy/src/index.ts @@ -0,0 +1,49 @@ +const UPSTREAM_BASE = "https://supermemory.mintlify.dev" +const CACHE_TTL = 300 // 5 minutes +const FETCH_TIMEOUT_MS = 10000 // 10 seconds + +export default { + async fetch(request: Request): Promise { + const url = new URL(request.url) + const proxyUrl = UPSTREAM_BASE + url.pathname + url.search + + const cache = caches.default + + if (request.method === "GET") { + const cached = await cache.match(request) + if (cached) return cached + } + + const controller = new AbortController() + const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS) + + try { + const proxyRequest = new Request(proxyUrl, request) + const response = await fetch(proxyRequest, { signal: controller.signal }) + + if (request.method === "GET" && response.ok) { + const cachedResponse = new Response(response.clone().body, response) + cachedResponse.headers.set( + "Cache-Control", + `public, max-age=${CACHE_TTL}`, + ) + await cache.put(request, cachedResponse) + } + + return response + } catch (e) { + if (e instanceof Error && e.name === "AbortError") { + return new Response( + "Documentation temporarily unavailable. Please try again in a moment.", + { + status: 504, + headers: { "Content-Type": "text/plain; charset=utf-8" }, + }, + ) + } + throw e + } finally { + clearTimeout(timeoutId) + } + }, +} diff --git a/apps/docs-proxy/tsconfig.json b/apps/docs-proxy/tsconfig.json new file mode 100644 index 000000000..91626ee12 --- /dev/null +++ b/apps/docs-proxy/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "lib": ["ESNext"], + "types": ["@cloudflare/workers-types"], + "outDir": "dist", + "rootDir": "src" + }, + "include": ["src/**/*"], + "exclude": ["node_modules"] +} diff --git a/apps/docs-proxy/wrangler.jsonc b/apps/docs-proxy/wrangler.jsonc new file mode 100644 index 000000000..350c5a910 --- /dev/null +++ b/apps/docs-proxy/wrangler.jsonc @@ -0,0 +1,11 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "supermemory-docs-mintlify", + "main": "src/index.ts", + "compatibility_date": "2025-06-20", + "compatibility_flags": [], + + "observability": { + "enabled": true + } +} diff --git a/bun.lock b/bun.lock index c6f5c06e2..484f683f5 100644 --- a/bun.lock +++ b/bun.lock @@ -83,6 +83,15 @@ "typescript": "^5.9.2", }, }, + "apps/docs-proxy": { + "name": "@repo/docs-proxy", + "version": "1.0.0", + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "typescript": "^5.8.3", + "wrangler": "^4.4.0", + }, + }, "apps/mcp": { "name": "supermemory-mcp", "version": "4.0.0", @@ -1448,6 +1457,8 @@ "@repo/docs": ["@repo/docs@workspace:apps/docs"], + "@repo/docs-proxy": ["@repo/docs-proxy@workspace:apps/docs-proxy"], + "@repo/hooks": ["@repo/hooks@workspace:packages/hooks"], "@repo/lib": ["@repo/lib@workspace:packages/lib"], @@ -5442,6 +5453,8 @@ "@repo/docs/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "@repo/docs-proxy/typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + "@repo/lib/better-auth": ["better-auth@1.3.3", "", { "dependencies": { "@better-auth/utils": "0.2.5", "@better-fetch/fetch": "^1.1.18", "@noble/ciphers": "^0.6.0", "@noble/hashes": "^1.8.0", "@simplewebauthn/browser": "^13.0.0", "@simplewebauthn/server": "^13.0.0", "better-call": "^1.0.12", "defu": "^6.1.4", "jose": "^5.9.6", "kysely": "^0.28.1", "nanostores": "^0.11.3", "zod": "^4.0.5" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["react", "react-dom"] }, "sha512-q1aD2nNpGfEI2ckYu+pBjN+23CIRctOpmREkWyJDJdoYW1q9EPs1Xdb+KhFztg2rMmsoUN8I9Xm5mUWMxiWuLw=="], "@repo/web/@ai-sdk/google": ["@ai-sdk/google@3.0.64", "", { "dependencies": { "@ai-sdk/provider": "3.0.8", "@ai-sdk/provider-utils": "4.0.23" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-CbR82EgGPNrj/6q0HtclwuCqe0/pDShyv3nWDP/A9DroujzWXnLMlUJVrgPOsg4b40zQCwwVs2XSKCxvt/4QaA=="],