Conversation
Co-authored-by: ascorbic <ascorbic@users.noreply.github.com>
🦋 Changeset detectedLatest commit: fd215ce The changes in this PR will be included in the next version bump. This PR includes changesets to release 13 packages
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 |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
docs | fd215ce | Apr 30 2026, 10:23 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-perf-coordinator | fd215ce | Apr 30 2026, 10:23 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-playground | fd215ce | Apr 30 2026, 10:24 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-demo-cache | fd215ce | Apr 30 2026, 10:24 PM |
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ✅ Deployment successful! View logs |
emdash-i18n | fd215ce | Apr 30 2026, 10:23 PM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
There was a problem hiding this comment.
Pull request overview
Fixes a Cloudflare Workers-specific bug where EmDashRuntime.invalidateManifest() attempted to delete the persisted manifest cache row via an unawaited promise that could be canceled at response teardown, leaving options.emdash:manifest_cache stale across cold isolates.
Changes:
- Route the persisted manifest-cache delete through
after()so it is registered withctx.waitUntilunder workerd. - Add a unit test that asserts
invalidateManifest()always defers its DB cleanup viaafter()and that the options row is removed. - Add a changeset for a patch release of
emdash.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
| packages/core/src/emdash-runtime.ts | Uses after(async () => ...) to perform the persisted manifest-cache delete so it survives Cloudflare Workers response teardown. |
| packages/core/tests/unit/runtime/invalidate-manifest.test.ts | New unit test reproducing issue #873 and locking in the after()-based invalidation contract. |
| .changeset/issue-873-invalidate-manifest-after.md | Patch changeset documenting the fix and user impact. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Summary
Reproduced and fixed.
Bug confirmed.
EmDashRuntime.invalidateManifest()atpackages/core/src/emdash-runtime.ts:1571started the D1 row delete as a bare unawaited promise. EmDash already has theafter()helper (packages/core/src/after.ts) which routes deferred work throughctx.waitUntilunder workerd, butinvalidateManifest()didn't use it. On Cloudflare Workers the delete promise gets cancelled at response-teardown, sooptions.emdash:manifest_cachestays at the pre-mutation snapshot. The reporter is also right that the existing_manifestCacheKeycheck at line ~1294 doesn't include schema content (only commit + plugin versions + i18n), so stale rows pass the check on every cold isolate.The reporter's pointer to a "second site at line ~993" is an off-by-line — line 1001 is already correctly
awaited inside the cold-start init closure. OnlyinvalidateManifest()had the bug.Reproduction. Wrote a TDD-style failing test in
packages/core/tests/unit/runtime/invalidate-manifest.test.tsthat:after()viavi.mock.options.emdash:manifest_cachewith a stale row.runtime.invalidateManifest().after()was invoked, and (b) after draining microtasks, the row is gone.Confirmed it failed before the fix (
expected "vi.fn()" to be called 1 times, but got 0 times) and passes after.Fix. Replaced the fire-and-forget pattern with an
after(async () => { ... })block. The deletion now extends the worker's lifetime viawaitUntilon Cloudflare and stays fire-and-forget on Node (where it works fine because the process keeps running). Comment updated to call out why this matters and points at the_manifestCacheKeyschema-content gap.Out of scope (per the issue's own note). The warm-sibling-isolate problem —
_cachedManifesthas no TTL or cross-isolate signal — is a separate bug class and isn't addressed here. The reporter said they'd file it separately.Verification.
tests/unit/after.test.tsstill passes.pnpm --filter emdash typecheckclean.pnpm --silent lint:json | jq '.diagnostics | length'returns 14 — identical to baseline (all pre-existingnull:nulltype-resolution diagnostics in unrelated packages).tests/unit/astro/vite-config.test.tsand are pre-existing in this CI environment (admin's build script usesnode --run, which the Node version here doesn't accept, so@emdash-cms/admin/dist/index.jsis missing). Confirmed by stashing my change and reproducing.pnpm format.Changeset added at
.changeset/issue-873-invalidate-manifest-after.md(patch bump onemdash).Files changed:
packages/core/src/emdash-runtime.ts—invalidateManifest()now defers the D1 delete viaafter().packages/core/tests/unit/runtime/invalidate-manifest.test.ts— new TDD test..changeset/issue-873-invalidate-manifest-after.md— changeset.Closes #873
github run