diff --git a/.changeset/angry-socks-sell.md b/.changeset/angry-socks-sell.md new file mode 100644 index 000000000..d04d01cb2 --- /dev/null +++ b/.changeset/angry-socks-sell.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': minor +--- + +Expose cf metadata and Cloudflare Worker Cache API through `caches` in runtime. diff --git a/packages/integrations/cloudflare/package.json b/packages/integrations/cloudflare/package.json index 91bb1ae6a..46f401674 100644 --- a/packages/integrations/cloudflare/package.json +++ b/packages/integrations/cloudflare/package.json @@ -39,6 +39,7 @@ }, "dependencies": { "@astrojs/underscore-redirects": "^0.1.0", + "@cloudflare/workers-types": "^4.20230518.0", "esbuild": "^0.17.12", "tiny-glob": "^0.2.9" }, diff --git a/packages/integrations/cloudflare/src/runtime.ts b/packages/integrations/cloudflare/src/runtime.ts index ddf372cb4..cd3dfff47 100644 --- a/packages/integrations/cloudflare/src/runtime.ts +++ b/packages/integrations/cloudflare/src/runtime.ts @@ -1,8 +1,12 @@ +import type { Cache, CacheStorage, IncomingRequestCfProperties } from '@cloudflare/workers-types'; + export type WorkerRuntime = { name: 'cloudflare'; env: T; waitUntil(promise: Promise): void; passThroughOnException(): void; + caches?: CacheStorage & { default: Cache }; + cf?: IncomingRequestCfProperties; }; export type PagesRuntime = { @@ -13,6 +17,8 @@ export type PagesRuntime = { data: U; waitUntil(promise: Promise): void; next(request: Request): void; + caches?: CacheStorage & { default: Cache }; + cf?: IncomingRequestCfProperties; }; export function getRuntime( diff --git a/packages/integrations/cloudflare/src/server.advanced.ts b/packages/integrations/cloudflare/src/server.advanced.ts index 669e95ebc..0fc7af855 100644 --- a/packages/integrations/cloudflare/src/server.advanced.ts +++ b/packages/integrations/cloudflare/src/server.advanced.ts @@ -1,4 +1,5 @@ import type { SSRManifest } from 'astro'; +import type { Request as CFRequest } from '@cloudflare/workers-types'; import { App } from 'astro/app'; import { getProcessEnvProxy, isNode } from './util.js'; @@ -14,7 +15,7 @@ type Env = { export function createExports(manifest: SSRManifest) { const app = new App(manifest); - const fetch = async (request: Request, env: Env, context: any) => { + const fetch = async (request: Request & CFRequest, env: Env, context: any) => { process.env = env as any; const { pathname } = new URL(request.url); @@ -31,7 +32,13 @@ export function createExports(manifest: SSRManifest) { Symbol.for('astro.clientAddress'), request.headers.get('cf-connecting-ip') ); - Reflect.set(request, Symbol.for('runtime'), { env, name: 'cloudflare', ...context }); + Reflect.set(request, Symbol.for('runtime'), { + env, + name: 'cloudflare', + caches, + cf: request.cf, + ...context, + }); let response = await app.render(request, routeData); if (app.setCookieHeaders) { diff --git a/packages/integrations/cloudflare/src/server.directory.ts b/packages/integrations/cloudflare/src/server.directory.ts index 2a0e06e5c..1c134c6d3 100644 --- a/packages/integrations/cloudflare/src/server.directory.ts +++ b/packages/integrations/cloudflare/src/server.directory.ts @@ -1,4 +1,5 @@ import type { SSRManifest } from 'astro'; +import type { Request as CFRequest } from '@cloudflare/workers-types'; import { App } from 'astro/app'; import { getProcessEnvProxy, isNode } from './util.js'; @@ -14,7 +15,7 @@ export function createExports(manifest: SSRManifest) { next, ...runtimeEnv }: { - request: Request; + request: Request & CFRequest; next: (request: Request) => void; } & Record) => { process.env = runtimeEnv.env as any; @@ -36,6 +37,8 @@ export function createExports(manifest: SSRManifest) { ...runtimeEnv, name: 'cloudflare', next, + caches, + cf: request.cf, }); let response = await app.render(request, routeData); diff --git a/packages/integrations/cloudflare/test/cf.js b/packages/integrations/cloudflare/test/cf.js new file mode 100644 index 000000000..ba4efedef --- /dev/null +++ b/packages/integrations/cloudflare/test/cf.js @@ -0,0 +1,35 @@ +import { loadFixture, runCLI } from './test-utils.js'; +import { expect } from 'chai'; +import * as cheerio from 'cheerio'; +import cloudflare from '../dist/index.js'; + +describe('Cf metadata and caches', () => { + /** @type {import('./test-utils').Fixture} */ + let fixture; + + before(async () => { + fixture = await loadFixture({ + root: './fixtures/cf/', + output: 'server', + adapter: cloudflare(), + }); + await fixture.build(); + }); + + it('Load cf and caches API', async () => { + const { ready, stop } = runCLI('./fixtures/cf/', { silent: false, port: 8788 }); + + try { + await ready; + let res = await fetch(`http://localhost:8788/`); + expect(res.status).to.equal(200); + let html = await res.text(); + let $ = cheerio.load(html); + // console.log($('#cf').text(), html); + expect($('#cf').text()).to.contain('city'); + expect($('#hasCache').text()).to.equal('true'); + } finally { + stop(); + } + }); +}); diff --git a/packages/integrations/cloudflare/test/fixtures/cf/astro.config.mjs b/packages/integrations/cloudflare/test/fixtures/cf/astro.config.mjs new file mode 100644 index 000000000..f92829843 --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/cf/astro.config.mjs @@ -0,0 +1,8 @@ +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; + + +export default defineConfig({ + adapter: cloudflare(), + output: 'server', +}); diff --git a/packages/integrations/cloudflare/test/fixtures/cf/package.json b/packages/integrations/cloudflare/test/fixtures/cf/package.json new file mode 100644 index 000000000..cf8ae0082 --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/cf/package.json @@ -0,0 +1,9 @@ +{ + "name": "@test/astro-cloudflare-cf", + "version": "0.0.0", + "private": true, + "dependencies": { + "@astrojs/cloudflare": "workspace:*", + "astro": "workspace:*" + } +} diff --git a/packages/integrations/cloudflare/test/fixtures/cf/src/pages/index.astro b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/index.astro new file mode 100644 index 000000000..c9e67bb05 --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/index.astro @@ -0,0 +1,14 @@ +--- +import { getRuntime } from '@astrojs/cloudflare/runtime'; +const runtime = getRuntime(Astro.request); +--- + + + Testing + + +

Testing

+
{JSON.stringify(runtime.cf)}
+
{!!runtime.caches}
+ + diff --git a/packages/integrations/cloudflare/test/test-utils.js b/packages/integrations/cloudflare/test/test-utils.js index b4628825c..399b97812 100644 --- a/packages/integrations/cloudflare/test/test-utils.js +++ b/packages/integrations/cloudflare/test/test-utils.js @@ -19,9 +19,9 @@ const wranglerPath = fileURLToPath( new URL('../node_modules/wrangler/bin/wrangler.js', import.meta.url) ); -export function runCLI(basePath, { silent }) { +export function runCLI(basePath, { silent, port = 8787 }) { const script = fileURLToPath(new URL(`${basePath}/dist/_worker.js`, import.meta.url)); - const p = spawn('node', [wranglerPath, 'dev', '-l', script]); + const p = spawn('node', [wranglerPath, 'dev', '-l', script, '--port', port]); p.stderr.setEncoding('utf-8'); p.stdout.setEncoding('utf-8'); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index be3b20224..f5d6fee1d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,8 +1,4 @@ -lockfileVersion: '6.1' - -settings: - autoInstallPeers: false - excludeLinksFromLockfile: false +lockfileVersion: '6.0' overrides: tsconfig-resolver>type-fest: 3.0.0 @@ -3589,6 +3585,9 @@ importers: '@astrojs/underscore-redirects': specifier: ^0.1.0 version: link:../../underscore-redirects + '@cloudflare/workers-types': + specifier: ^4.20230518.0 + version: 4.20230518.0 esbuild: specifier: ^0.17.12 version: 0.17.12 @@ -3627,6 +3626,15 @@ importers: specifier: workspace:* version: link:../../../../../astro + packages/integrations/cloudflare/test/fixtures/cf: + dependencies: + '@astrojs/cloudflare': + specifier: workspace:* + version: link:../../.. + astro: + specifier: workspace:* + version: link:../../../../../astro + packages/integrations/cloudflare/test/fixtures/no-output: dependencies: '@astrojs/cloudflare': @@ -7226,6 +7234,10 @@ packages: mime: 3.0.0 dev: true + /@cloudflare/workers-types@4.20230518.0: + resolution: {integrity: sha512-A0w1V+5SUawGaaPRlhFhSC/SCDT9oQG8TMoWOKFLA4qbqagELqEAFD4KySBIkeVOvCBLT1DZSYBMCxbXddl0kw==} + dev: false + /@colors/colors@1.5.0: resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} @@ -18393,3 +18405,7 @@ packages: dependencies: solid-js: 1.7.4 dev: false + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false