From 9f04df4af4f2d6eb7d00f78f3fd18a66d7f50bfa Mon Sep 17 00:00:00 2001 From: Alexander Niebuhr Date: Mon, 25 Sep 2023 17:21:48 +0200 Subject: [PATCH 1/2] feature(cloudflare): add Caches bindings --------- Co-authored-by: Sarah Rainsberger --- .changeset/yellow-kiwis-pretend.md | 5 ++++ packages/integrations/cloudflare/src/index.ts | 23 +++---------------- .../integrations/cloudflare/test/cf.test.js | 13 ++++++++--- .../test/fixtures/cf/src/pages/caches.astro | 15 ++++++++++++ .../test/fixtures/cf/src/pages/index.astro | 3 +-- 5 files changed, 34 insertions(+), 25 deletions(-) create mode 100644 .changeset/yellow-kiwis-pretend.md create mode 100644 packages/integrations/cloudflare/test/fixtures/cf/src/pages/caches.astro diff --git a/.changeset/yellow-kiwis-pretend.md b/.changeset/yellow-kiwis-pretend.md new file mode 100644 index 000000000000..cd5ee554b693 --- /dev/null +++ b/.changeset/yellow-kiwis-pretend.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': minor +--- + +Introduces support for local Caches bindings. Enhances development experience by allowing direct integration with `astro dev`. diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 82bab441f5a9..7a364d42e3e2 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -1,9 +1,6 @@ import type { AstroConfig, AstroIntegration, RouteData } from 'astro'; import { createRedirectsFromAstroRoutes } from '@astrojs/underscore-redirects'; -import { CacheStorage } from '@miniflare/cache'; -import { NoOpLog } from '@miniflare/shared'; -import { MemoryStorage } from '@miniflare/storage-memory'; import { AstroError } from 'astro/errors'; import esbuild from 'esbuild'; import { Miniflare } from 'miniflare'; @@ -56,17 +53,6 @@ interface BuildConfig { split?: boolean; } -class StorageFactory { - storages = new Map(); - - storage(namespace: string) { - let storage = this.storages.get(namespace); - if (storage) return storage; - this.storages.set(namespace, (storage = new MemoryStorage())); - return storage; - } -} - export default function createIntegration(args?: Options): AstroIntegration { let _config: AstroConfig; let _buildConfig: BuildConfig; @@ -140,6 +126,7 @@ export default function createIntegration(args?: Options): AstroIntegration { script: '', cache: true, cachePersist: true, + cacheWarnUsage: true, d1Databases: D1Bindings, d1Persist: true, r2Buckets: R2Bindings, @@ -161,6 +148,7 @@ export default function createIntegration(args?: Options): AstroIntegration { const namespace = await _mf.getKVNamespace(KVBinding); Reflect.set(bindingsEnv, KVBinding, namespace); } + const mfCache = await _mf.getCaches(); process.env.PWD = originalPWD; const clientLocalsSymbol = Symbol.for('astro.locals'); @@ -183,12 +171,7 @@ export default function createIntegration(args?: Options): AstroIntegration { waitUntil: (_promise: Promise) => { return; }, - caches: new CacheStorage( - { cache: true, cachePersist: false }, - new NoOpLog(), - new StorageFactory(), - {} - ), + caches: mfCache, }, }); next(); diff --git a/packages/integrations/cloudflare/test/cf.test.js b/packages/integrations/cloudflare/test/cf.test.js index a9f5607373f5..0276edd6a5a6 100644 --- a/packages/integrations/cloudflare/test/cf.test.js +++ b/packages/integrations/cloudflare/test/cf.test.js @@ -63,13 +63,20 @@ describe('Astro Cloudflare Runtime', () => { await devServer?.stop(); }); - it('Populates CF, Vars & Bindings', async () => { + it('adds cf object', async () => { let res = await fixture.fetch('/'); expect(res.status).to.equal(200); let html = await res.text(); let $ = cheerio.load(html); - expect($('#hasRuntime').text()).to.equal('true'); - expect($('#hasCache').text()).to.equal('true'); + expect($('#hasCF').text()).to.equal('true'); + }); + + it('adds cache mocking', async () => { + let res = await fixture.fetch('/caches'); + expect(res.status).to.equal(200); + let html = await res.text(); + let $ = cheerio.load(html); + expect($('#hasCACHE').text()).to.equal('true'); }); it('adds D1 mocking', async () => { diff --git a/packages/integrations/cloudflare/test/fixtures/cf/src/pages/caches.astro b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/caches.astro new file mode 100644 index 000000000000..743111721e53 --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/caches.astro @@ -0,0 +1,15 @@ +--- +const runtime = Astro.locals.runtime; +--- + + + + + + + CACHES + + +
{!!runtime.caches}
+ + diff --git a/packages/integrations/cloudflare/test/fixtures/cf/src/pages/index.astro b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/index.astro index 0e904752fb29..5ba11985f0c6 100644 --- a/packages/integrations/cloudflare/test/fixtures/cf/src/pages/index.astro +++ b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/index.astro @@ -7,7 +7,6 @@ const runtime = Astro.locals.runtime;

Testing

-
{!!runtime.cf?.colo}
-
{!!runtime.caches}
+
{!!runtime.cf?.colo}
From 1de957bc05c1507c04169377a5f6877b62b99d16 Mon Sep 17 00:00:00 2001 From: Alexander Niebuhr Date: Mon, 2 Oct 2023 16:02:22 +0200 Subject: [PATCH 2/2] feat(cloudflare): add local mocking for DO bindings (#8690) --------- Co-authored-by: Sarah Rainsberger --- .changeset/hot-readers-live.md | 5 +++++ packages/integrations/cloudflare/README.md | 1 + packages/integrations/cloudflare/src/index.ts | 18 ++++++++++++++++-- .../cloudflare/src/utils/parser.ts | 17 +++++++++++++++++ .../integrations/cloudflare/test/cf.test.js | 10 ++++++++++ .../test/fixtures/cf/src/pages/do.astro | 15 +++++++++++++++ .../cloudflare/test/fixtures/cf/wrangler.toml | 7 +++++++ 7 files changed, 71 insertions(+), 2 deletions(-) create mode 100644 .changeset/hot-readers-live.md create mode 100644 packages/integrations/cloudflare/test/fixtures/cf/src/pages/do.astro diff --git a/.changeset/hot-readers-live.md b/.changeset/hot-readers-live.md new file mode 100644 index 000000000000..f510d77c1fa8 --- /dev/null +++ b/.changeset/hot-readers-live.md @@ -0,0 +1,5 @@ +--- +'@astrojs/cloudflare': minor +--- + +Introduces support for local Durable Objects bindings. Enhances development experience by allowing direct integration with `astro dev`. diff --git a/packages/integrations/cloudflare/README.md b/packages/integrations/cloudflare/README.md index b1d9969833d7..10a381a7b69e 100644 --- a/packages/integrations/cloudflare/README.md +++ b/packages/integrations/cloudflare/README.md @@ -219,6 +219,7 @@ Currently supported bindings: - [Cloudflare D1](https://developers.cloudflare.com/d1/) - [Cloudflare R2](https://developers.cloudflare.com/r2/) - [Cloudflare Workers KV](https://developers.cloudflare.com/kv/) +- [Cloudflare Durable Objects](https://developers.cloudflare.com/durable-objects/) You can access the runtime from Astro components through `Astro.locals` inside any .astro` file. diff --git a/packages/integrations/cloudflare/src/index.ts b/packages/integrations/cloudflare/src/index.ts index 7a364d42e3e2..59cd92ce33d0 100644 --- a/packages/integrations/cloudflare/src/index.ts +++ b/packages/integrations/cloudflare/src/index.ts @@ -12,7 +12,13 @@ import glob from 'tiny-glob'; import { getAdapter } from './getAdapter.js'; import { deduplicatePatterns } from './utils/deduplicatePatterns.js'; import { getCFObject } from './utils/getCFObject.js'; -import { getD1Bindings, getEnvVars, getKVBindings, getR2Bindings } from './utils/parser.js'; +import { + getD1Bindings, + getDOBindings, + getEnvVars, + getKVBindings, + getR2Bindings, +} from './utils/parser.js'; import { prependForwardSlash } from './utils/prependForwardSlash.js'; import { rewriteWasmImportPath } from './utils/rewriteWasmImportPath.js'; import { wasmModuleLoader } from './utils/wasm-module-loader.js'; @@ -113,7 +119,7 @@ export default function createIntegration(args?: Options): AstroIntegration { const D1Bindings = await getD1Bindings(); const R2Bindings = await getR2Bindings(); const KVBindings = await getKVBindings(); - + const DOBindings = await getDOBindings(); let bindingsEnv = new Object({}); // fix for the error "kj/filesystem-disk-unix.c++:1709: warning: PWD environment variable doesn't match current directory." @@ -133,6 +139,8 @@ export default function createIntegration(args?: Options): AstroIntegration { r2Persist: true, kvNamespaces: KVBindings, kvPersist: true, + durableObjects: DOBindings, + durableObjectsPersist: true, }); await _mf.ready; @@ -148,6 +156,12 @@ export default function createIntegration(args?: Options): AstroIntegration { const namespace = await _mf.getKVNamespace(KVBinding); Reflect.set(bindingsEnv, KVBinding, namespace); } + for (const key in DOBindings) { + if (Object.prototype.hasOwnProperty.call(DOBindings, key)) { + const DO = await _mf.getDurableObjectNamespace(key); + Reflect.set(bindingsEnv, key, DO); + } + } const mfCache = await _mf.getCaches(); process.env.PWD = originalPWD; diff --git a/packages/integrations/cloudflare/src/utils/parser.ts b/packages/integrations/cloudflare/src/utils/parser.ts index af64e68c68aa..de239a1ee707 100644 --- a/packages/integrations/cloudflare/src/utils/parser.ts +++ b/packages/integrations/cloudflare/src/utils/parser.ts @@ -172,3 +172,20 @@ export async function getKVBindings() { ); return bindings; } + +export function getDOBindings(): Record< + string, + { scriptName?: string | undefined; unsafeUniqueKey?: string | undefined; className: string } +> { + const { rawConfig } = parseConfig(); + if (!rawConfig) return {}; + if (!rawConfig?.durable_objects) return {}; + const output = new Object({}) as Record< + string, + { scriptName?: string | undefined; unsafeUniqueKey?: string | undefined; className: string } + >; + for (const binding of rawConfig?.durable_objects.bindings) { + Reflect.set(output, binding.name, { className: binding.class_name }); + } + return output; +} diff --git a/packages/integrations/cloudflare/test/cf.test.js b/packages/integrations/cloudflare/test/cf.test.js index 0276edd6a5a6..0078f30245b3 100644 --- a/packages/integrations/cloudflare/test/cf.test.js +++ b/packages/integrations/cloudflare/test/cf.test.js @@ -114,4 +114,14 @@ describe('Astro Cloudflare Runtime', () => { expect($('#hasPRODKV').text()).to.equal('true'); expect($('#hasACCESS').text()).to.equal('true'); }); + + it('adds DO mocking', async () => { + expect(await fixture.pathExists('../.mf/do')).to.be.true; + + let res = await fixture.fetch('/do'); + expect(res.status).to.equal(200); + let html = await res.text(); + let $ = cheerio.load(html); + expect($('#hasDO').text()).to.equal('true'); + }); }); diff --git a/packages/integrations/cloudflare/test/fixtures/cf/src/pages/do.astro b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/do.astro new file mode 100644 index 000000000000..e338c8e8f9cc --- /dev/null +++ b/packages/integrations/cloudflare/test/fixtures/cf/src/pages/do.astro @@ -0,0 +1,15 @@ +--- +const runtime = Astro.locals.runtime; +--- + + + + + + + DO + + +
{!!runtime.env.DO}
+ + diff --git a/packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml b/packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml index a0fc8ddb5cd9..17fd88ea5cb9 100644 --- a/packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml +++ b/packages/integrations/cloudflare/test/fixtures/cf/wrangler.toml @@ -28,3 +28,10 @@ bucket_name = '' binding = 'R2_PROD' # <~ valid JavaScript variable name bucket_name = '' +[[durable_objects.bindings]] +name = "DO" +class_name = "DurableObjectExample" + +[[durable_objects.bindings]] +name = "DO_PROD" +class_name = "DurableObjectProductionExample"