Skip to content

fix(cloudflare): use configured database in sandbox plugin bridge - #4725

Open
emdash-autofix[bot] wants to merge 3 commits into
mainfrom
emdash-bot/issue-1623
Open

emdash-autofix[bot] wants to merge 3 commits into
mainfrom
emdash-bot/issue-1623

Conversation

@emdash-autofix

@emdash-autofix emdash-autofix Bot commented Nov 7, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes Cloudflare sandbox plugins using env.DB regardless of the site's database adapter. Threads the configured database descriptor through core runtime and runner into bridge props, resolves the build-selected virtual dialect, and creates a database scope for each database RPC. Hyperdrive pools close in finally; D1 and DO SQL reads use primary routing so separate plugin calls observe preceding writes. Bridge-owned connections and DO stubs are not cached across RPCs. Only the configured adapter is bundled, so D1 sites do not acquire a dependency on pg.

Replaces direct D1 queries with shared core access/repository helpers and portable Kysely queries, preserving capability checks, plugin namespaces, content write fences, and the existing content-read fallback for partially migrated schemas. Removes the D1-only notes from the adapter JSDoc and deployment docs. Includes a patch changeset for both changed packages.

Runtime-owned mutations (content actions, taxonomy writes, moderation, and content creation) continue using the existing invocation-specific PluginRuntimeBridge RPC target, not the loopback callback fallback. That target already restores the originating request's ALS context with AsyncLocalStorage.snapshot(). A new regression test clears the request context on the plugin side and verifies that concurrent runtime callbacks still query their respective real databases. No callback connection-lifecycle change is needed on that production path.

The Node/workerd runner continues using its supplied host database: its handlers run in Node, where pools are not bound to a Cloudflare event. Production Cloudflare bridge calls need separate scopes; N database bridge calls can therefore incur N connection setups with Hyperdrive. The old descriptor-less D1 fallback is retained for compatibility; this PR does not redesign standalone legacy callback APIs or their configuration diagnostics. Misconfigured D1 coalescing without sessions can still emit adapter warnings on each new scope.

Closes #1623

Type of change

  • Bug fix
  • Feature (requires an accepted feature plan or RFC)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Your connection to this change

  • I use EmDash and ran into the problem this PR addresses
  • I maintain a tool, dependency, or service that EmDash uses, and this change concerns that integration
  • A Maintainer approved this specific change before I opened the PR

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (not applicable: no UI changes)
  • I have added and reviewed the user-facing changeset
  • New features link to their merged design PR (not applicable: bug fix)
  • I have included screenshots below if this PR changes the UI (not applicable)

AI assistance disclosure

  • This PR includes AI-generated code or text — model/tool: cloudflare-ai-gateway:gpt-6.1-sol via the EmDash bot (Pi Durable)

Screenshots / test output

Screenshots: Not applicable.

  • Root workspace build passes with NODE_OPTIONS=--max-old-space-size=8192 (the default heap was insufficient).
  • Root package typecheck and type-aware lint pass; lint:quick reports zero diagnostics.
  • Cloudflare suite: 574 passed, 3 skipped (including opt-in PostgreSQL test when EMDASH_TEST_PG is unset).
  • Core sandbox runner/runtime subset: 11 passed.
  • Configured-adapter regressions: 6 passed with EMDASH_TEST_PG pointing at a local real PostgreSQL 18 server. D1/DO transport shims execute real SQLite queries; PostgreSQL exercises the actual Hyperdrive adapter and pg pools. Covers storage/KV/settings, content creation/reads/deletion, media metadata/reads/deletion, decoy or absent DB bindings, concurrent/sequential failed pool cleanup, and missing Hyperdrive bindings.
  • Added runtime-to-runner and runner-to-loopback descriptor forwarding checks.
  • Built the actual Cloudflare demo production Worker successfully, including its PluginBridge export and the configured virtual dialect. Font fetching emitted warnings because external font hosts are unavailable in the sandbox; the build completed.

A Maintainer (@ascorbic) assigned this issue to the EmDash bot.

Threaded the configured adapter into Cloudflare sandbox bridge props and replaced hardcoded D1 queries with event-scoped adapter connections, shared core helpers, and portable Kysely queries. Hyperdrive pools close after each bridge operation, including failures; primary routing preserves read-after-write consistency. Removed D1-only documentation and added regression tests and a changeset. Verified root build/typecheck/lint, Cloudflare tests (574 passed), core sandbox/runtime tests (11 passed), actual SQLite/PostgreSQL adapter regressions (6 passed), and a production Cloudflare demo Worker build. Independent follow-up review found all earlier findings addressed or reasonably answered.

EmDash-Bot-Case: 1198246700-issue-4745615752
EmDash-Bot-Candidate: cand-1-8231509db6
@emdash-autofix emdash-autofix Bot added the bot:fix Assign this issue or pull request to the EmDash bot to fix label Nov 7, 2026
@emdash-autofix

emdash-autofix Bot commented Nov 7, 2026 •

Copy link
Copy Markdown
Contributor Author

EmDash bot: Ready for human review

Plan

Startup regression fixed and verified at 23 modules/1,818,842 bytes. Cloudflare and plugin-test suites, real PostgreSQL adapter tests, root build/typecheck/lint all pass. Core CI setup timeouts pass locally (194 tests); aggregate failure n…

  • Inspect bridge, adapters, and existing tests
  • Wire configured adapter and replace D1-only bridge queries
  • Add regression coverage and run focused checks
  • Review change and submit candidate
  • Reproduce and fix plugin-test wiring and stale documentation
  • Verify review fixes, reply, and publish updated candidate
  • Restore lazy bridge database imports to meet the Worker startup budget
  • Verify startup and database regressions, review, and publish

Status

  • Required checks passed, the review bot approved the current head, and the head merges cleanly.

Next action: A maintainer will review and approve the pull request.

Details
Candidate cand-3-ba50d0c441 (tree f2f8c64885cb)
Published head 7ae6f35c0a01
Reviewer's latest review of head approved
Reporter confirmation not yet
Human approval required before merge

Required checks on the current head:

  • Typecheck: succeeded
  • Tests: succeeded
  • Smoke Tests: succeeded
  • Browser Tests: succeeded
  • Format: succeeded
  • CLAssistant: succeeded
  • Measure: succeeded

This status is generated from recorded evidence. It is not a review and contains no instructions.

@changeset-bot

changeset-bot Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7ae6f35

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 12 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/plugin-test Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
create-emdash Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://emdash-bot-issue-1623.try.emdashcms.com, https://emdash-bot-issue-1623-emdash-playground.emdash-cms.workers.dev (commit 7ae6f35)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://2fc1c8e8.try.emdashcms.com, https://2fc1c8e8-emdash-playground.emdash-cms.workers.dev 7ae6f35 2026-11-07T09:16:41.016Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://09eb4602.try.emdashcms.com, https://09eb4602-emdash-playground.emdash-cms.workers.dev 0031080 2026-11-07T09:04:23.020Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://81c8e8c6.try.emdashcms.com, https://81c8e8c6-emdash-playground.emdash-cms.workers.dev f8f9d6e 2026-11-07T08:44:33.093Z Visit the dashboard ↗

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs 0031080 Nov 07 2026, 09:02 AM

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach fits EmDash's adapter architecture. The serializable DatabaseDescriptor is passed through runtime, runner and bridge props, and the bridge resolves the build-selected virtual:emdash/dialect. Each RPC opens its own scope with reads forced to the primary or an uncached connection, and the bridge reuses core's access and repository helpers. This matches the direction of #1623. The reviewers disagreed on the verdict. The openai reviewer approved with no findings from a static review. The anthropic reviewer found two problems, and both are kept here. The first is that @emdash-cms/plugin-test likely breaks, because its Vite plugin does not stub virtual:emdash/dialect. That reviewer could not run the suite to confirm it. The second is that plugin-sandbox.mdx still says the bridge uses the DB binding directly and that Hyperdrive is unsupported. The openai reviewer did not raise either point.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.


function loadDialectModule(): Promise<BridgeDialectModule> {
// @ts-ignore - virtual module
dialectModulePromise ??= import("virtual:emdash/dialect");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] This literal import("virtual:emdash/dialect") only resolves inside an Astro build that runs EmDash's virtual-module plugin. @emdash-cms/plugin-test runs PluginBridge through @cloudflare/vitest-plugin. Its runtimeVirtualModulePlugin (packages/plugin-test/src/config.ts) stubs wait-until, scheduler, config, env, build and object-cache, but not dialect.

The plugin-test runtime host passes config.database: { entrypoint, type: "sqlite", config: { binding: "DB" } } (packages/plugin-test/src/runtime-host.ts:469). After this PR, that descriptor reaches the bridge props, so every bridge database RPC in plugin-test takes this branch.

Vite's import analysis may also reject the unresolved literal dynamic import when it transforms the module, which would break the legacy env.DB path too. Either way, the published plugin test harness (and packages/plugin-test/test/host.test.ts) would lose storage, KV, content and media access. The anthropic reviewer could not run it to confirm. The openai reviewer did not raise this.

Two possible fixes:

  • Add a virtual:emdash/dialect entry to plugin-test's stub map that re-exports @emdash-cms/cloudflare/db/d1's createDialect/createRequestScopedDb.
  • Hide the specifier from static analysis, as media/usage/collection-deletion.ts does with a constant, and keep plugin-test on the descriptor-less path.

Either way, a plugin-test case that exercises bridge storage should guard this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed: a new runtime-host storage regression failed with Cannot find package 'virtual:emdash/dialect'. Added the harness virtual module re-exporting the real D1 factories. The regression now passes through Worker Loader and verifies the sandbox-written data through host inspection. The full plugin-test Node/workerd suites also pass (41 workerd tests), as do its typecheck and focused type-aware lint.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also checked resolution from an external plugin project with no direct Cloudflare dependency. A Vite build regression reproduced the bare-specifier resolution failure, so the virtual module now uses the D1 entrypoint resolved relative to plugin-test via import.meta.resolve(). Final verification passes: 5 Node tests, 41 workerd tests, package typecheck, and focused type-aware lint.

"@emdash-cms/cloudflare": patch
---

Fixes sandboxed plugins on Cloudflare to use the site's configured D1, Hyperdrive, or Durable Objects SQL database instead of always using the `DB` D1 binding. Plugin database operations now open an event-scoped connection, so Hyperdrive connections are not reused across bridge calls.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[needs fixing] The PR removes the D1-only notes from database.mdx and the adapter JSDoc. It misses the same claim in docs/src/content/docs/deployment/plugin-sandbox.mdx:93-95:

The bridge that sandboxed plugins use for database access talks to the D1 binding named DB directly, independent of the configured adapter. Sandboxed plugins are not available on a Hyperdrive deployment.

After this change, that statement contradicts shipped behavior and the changeset. It is also the page readers hit when setting up the sandbox runner. Remove the Aside, or replace it with a short note that the bridge uses the configured d1(), hyperdrive() or durableObjects() database. Also consider stating plainly in this changeset that sandboxed plugins now work on Hyperdrive deployments. That is the user-facing capability; "event-scoped connection" is an internal detail.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated both the database-access table and the stale Aside in plugin-sandbox.mdx to describe the configured D1, Hyperdrive, or Durable Objects SQL database. The changeset now explicitly says sandboxed plugins work on Hyperdrive deployments and includes the plugin-test patch.

@pkg-pr-new

pkg-pr-new Bot commented Nov 7, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@4725

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@4725

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@4725

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@4725

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@4725

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@4725

emdash

npm i https://pkg.pr.new/emdash@4725

create-emdash

npm i https://pkg.pr.new/create-emdash@4725

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@4725

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@4725

@emdash-cms/plugin-test

npm i https://pkg.pr.new/@emdash-cms/plugin-test@4725

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@4725

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@4725

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@4725

@emdash-cms/registry-loader

npm i https://pkg.pr.new/@emdash-cms/registry-loader@4725

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@4725

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@4725

upgrade-emdash

npm i https://pkg.pr.new/upgrade-emdash@4725

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@4725

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@4725

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@4725

@emdash-cms/plugin-ai-search

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-search@4725

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@4725

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@4725

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@4725

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@4725

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@4725

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@4725

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@4725

commit: 7ae6f35

Addressed PR review by supplying plugin-test's virtual dialect with a D1 entrypoint resolved relative to the package, covering both workerd storage integration and external-project Vite resolution with failing-before/passing-after regression tests. Corrected the remaining sandbox deployment documentation and made Hyperdrive support explicit in the changeset, including a plugin-test patch. Verified the root build, all 5 plugin-test Node tests and 41 workerd tests, package typecheck, focused type-aware lint, formatting, and zero root lint:quick diagnostics. Independent follow-up review cleared the significant findings; the pre-existing descriptor-less fallback diagnostic remains unchanged.

EmDash-Bot-Case: 1198246700-issue-4745615752
EmDash-Bot-Candidate: cand-2-73edfac72c

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach fits the adapter architecture and #1623. The serializable DatabaseDescriptor is forwarded to the bridge props, and the bridge resolves the build-selected virtual:emdash/dialect. Each RPC opens its own database scope, forces reads to the primary, and closes the scope in finally. env.DB remains only as the fallback when no descriptor is set. Both reviewers approved and neither raised any findings. Both earlier findings are fixed at this head. @emdash-cms/plugin-test now stubs virtual:emdash/dialect, resolving the D1 entrypoint relative to the harness package. Regression tests cover Worker Loader storage access and resolution from an external consumer project. The sandbox deployment page and the changeset now describe configured-adapter support, including Hyperdrive. Both reviews were static, and neither ran the test suite.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

Fixed the CI Measure regression by restoring lazy loading for bridge database helpers and SQL. Reproduced startup failure at 28 modules/2,272,211 bytes; the starter production build now passes startup:check at 23 modules/1,818,842 bytes. Verified root build/typecheck/lint, 574 Cloudflare tests, plugin-test's 5 Node and 41 workerd tests, and 6 real SQLite/PostgreSQL adapter regressions. The core shard's three PostgreSQL setup timeouts did not reproduce: both affected files pass locally (194 tests); Tests was only the aggregate failure. Independent review found no blockers.

EmDash-Bot-Case: 1198246700-issue-4745615752
EmDash-Bot-Candidate: cand-3-ba50d0c441

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach fits the adapter architecture and addresses #1623. The serializable DatabaseDescriptor is forwarded to the bridge props, and PluginBridge resolves the build-selected virtual:emdash/dialect. Each RPC opens its own database scope and closes it in finally. env.DB is used only as a fallback when no descriptor is set. Both reviewers approved with no findings. Both earlier findings are fixed at this head. @emdash-cms/plugin-test now stubs virtual:emdash/dialect, resolving the D1 entrypoint relative to the harness package, with Worker Loader and external-consumer regression tests. The sandbox docs and changeset now describe configured-adapter and Hyperdrive support. Both reviews were static, and neither ran the test suite.


Reviewed by anthropic/claude-opus-5-5 and openai/gpt-6.1-sol.

@emdash-autofix emdash-autofix Bot added triage/awaiting-reporter Fix pushed, waiting on reporter to verify and removed triage/awaiting-reporter Fix pushed, waiting on reporter to verify labels Nov 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/cloudflare area/core area/docs area/plugins bot:fix Assign this issue or pull request to the EmDash bot to fix cla: signed size/XL triage/awaiting-reporter Fix pushed, waiting on reporter to verify

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Sandboxed plugins on Cloudflare are D1-only

0 participants