chore: update Astro and adapters - #3105
Conversation
🦋 Changeset detectedLatest commit: 71a8080 The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 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 | c77cc16 | Sep 14 2026, 11:00 AM |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/auth-atproto
@emdash-cms/blocks
@emdash-cms/cloudflare
@emdash-cms/contentful-to-portable-text
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/plugin-cli
@emdash-cms/plugin-test
@emdash-cms/plugin-types
@emdash-cms/registry-client
@emdash-cms/registry-lexicons
@emdash-cms/registry-moderation
@emdash-cms/registry-verification
@emdash-cms/sandbox-workerd
@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-field-kit
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
Scope checkThis PR changes 4,251 lines across 9 files. Large PRs are harder to review and more likely to be closed without review. If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs. See CONTRIBUTING.md for contribution guidelines. |
There was a problem hiding this comment.
Approach is sound: Astro 7.3 removed app.pipeline.getCacheProvider, so resolving the configured cache provider from app.manifest.cacheProvider is the correct migration for the Cloudflare scheduled() handler. Caching the provider per isolate and invalidating tags incrementally after each batch matches the existing design and the PR's stated regression target. The changeset is concise and user-facing.
I found two blocking-ish contract/convention issues and one coverage suggestion:
- Peer dependency mismatch –
@emdash-cms/cloudflarestill declares"astro": ">=6.0.0-beta.0", but the new worker code callsapp.manifest.cacheProvider?.(), an Astro 7.3-era API. Consumers on Astro 6 will silently no-op cache invalidation without this range being narrowed. - Module-scope singleton – the new
cacheProvider(and the existingapp) are plain module-level variables, which violates AGENTS.md'sglobalThis+Symbol.forsingleton discipline for duplicated SSR/worker chunks. This also makes tests that want alternate provider states unreliable. - No-op coverage – the new regression test covers the invalidation path, but the documented "no-op when no cache provider is configured" path is no longer exercised after the mock was changed to always return a provider.
The dependency and docs-only changes are otherwise consistent with a routine Astro 7.3/Starlight 0.42 upgrade.
Findings
-
[needs fixing]
packages/cloudflare/package.json:126The package now calls
app.manifest.cacheProviderinsrc/worker.ts, which is an Astro 7.3 API, butpeerDependencies.astrostill allows Astro 6 beta and up. A consumer on Astro 6 will load the worker successfully (the optional chain won't throw), but scheduled cache invalidation will silently no-op because the provider is never resolved.Narrow the peer range to the Astro generation the code actually requires:
"astro": ">=7.3.0",(Use
>=7.3.2if the manifest surface shipped in that patch.) While here, consider whether@astrojs/cloudflare>=12.0.0is still compatible with the adapter version this code is built against. -
[needs fixing]
packages/cloudflare/src/worker.ts:22-23The new isolate-level cache-provider cache is a plain module-scope variable. AGENTS.md requires module-scope singletons to live on
globalThisbehind aSymbol.forkey, because Vite/Rollup can duplicate modules across worker/SSR chunks and produce two independentletvariables.Replace both
appandcacheProviderwith globalThis-backed helpers:const APP_KEY = Symbol.for("@emdash-cms/cloudflare:app"); const CACHE_PROVIDER_KEY = Symbol.for("@emdash-cms/cloudflare:cache-provider"); const g = globalThis as Record<symbol, unknown>; function getApp() { const existing = g[APP_KEY] as ReturnType<typeof createApp> | undefined; if (existing) return existing; const app = createApp(); g[APP_KEY] = app; return app; } async function loadCacheProvider() { const app = getApp(); const module = await app.manifest.cacheProvider?.(); return module?.default?.(app.manifest.cacheConfig?.options) ?? null; } function getCacheProvider() { const existing = g[CACHE_PROVIDER_KEY] as ReturnType<typeof loadCacheProvider> | undefined; if (existing) return existing; const provider = loadCacheProvider(); g[CACHE_PROVIDER_KEY] = provider; return provider; }Then in
invalidatePublishedTagscallconst provider = await getCacheProvider();instead ofcacheProvider ??= loadCacheProvider();. -
[suggestion]
packages/cloudflare/tests/worker-scheduled.test.ts:58The new regression test covers cache invalidation, but the docstring/code path "A no-op when no cache provider is configured" is no longer exercised after the mock was changed to always return a provider.
Add a sibling test that verifies the handler does not throw and does not call
cache.invalidatewhenmanifest.cacheProvideris absent. Because the production singleton is lazy, this will likely needvi.resetModules()inbeforeEach(or a test-only reset helper) to keep different provider states from leaking between tests.
There was a problem hiding this comment.
The approach is still sound: moving scheduled cache invalidation from the removed app.pipeline.getCacheProvider to app.manifest.cacheProvider and caching the resolved provider per isolate is the right migration for Astro 7.3. The author addressed the two concrete blockers from the prior review: the app and cacheProvider singletons now live on globalThis behind Symbol.for keys, and a regression test covers the no-op path.
Two issues remain. First, the Astro version contract is internally inconsistent: the changeset says “Astro 7.3 sites”, the PR description claims the app.manifest.cacheProvider surface is present in Astro 6.0.0 and 6.4.8, and the peer dependency range still allows Astro 6 beta. Either narrow the range or update the public docs to reflect supported versions. Second, getCacheProvider caches the loading promise before it resolves, so a transient failure in provider resolution becomes a permanent cache-invalidation outage for that worker isolate. Without node_modules, I cannot verify the Astro 6 claim directly; treat it as unverified rather than confirmed.
I reviewed the diff, the changed worker and test files, the changeset, the Cloudflare package manifest, and the docs llms.txt.ts/Astro config changes against AGENTS.md conventions.
|
Addressed the current-head findings in
I kept Astro 6 support after checking the published ~ 🤖 Codex |
There was a problem hiding this comment.
This is a re-review of the Astro 7.3 adapter upgrade. The approach is still sound: moving scheduled cache invalidation off the removed app.pipeline.getCacheProvider and onto app.manifest.cacheProvider while caching the resolved provider per isolate is the right migration.
The two concrete blockers from the prior review are now addressed:
appandcacheProviderlive onglobalThisbehind privately-namespacedSymbol.forkeys, so module duplication under Vite doesn't create multiple singletons.- A regression test covers the no-op path (no cache provider configured), and another test verifies that a transient provider-loading failure evicts the cached promise so the next scheduled tick retries.
I re-read the changed worker and tests, the Cloudflare package manifest, the changeset, and the docs config/page changes. The implementation is clean and the tests verify observable behavior rather than implementation details.
One item from the prior review is still open: the Astro version contract. The worker now reads app.manifest.cacheProvider, which the PR description asserts is present in Astro 6.0.0 and 6.4.8, but the @emdash-cms/cloudflare peer dependency still allows astro >=6.0.0-beta.0. That range includes prereleases the PR does not claim support for. Without node_modules I cannot verify the Astro 6 claim directly, but the mismatch between the allowed range and the claimed supported versions is a real contract risk: consumers on a 6.0.0 beta/rc could see scheduled cache invalidation throw. Narrowing the peer range (or providing docs that explicitly list the minimum supported Astro version) remains the right fix.
If that range is tightened, this is an LGTM.
Findings
-
[needs fixing]
packages/cloudflare/package.json:126The
scheduled()handler now depends onapp.manifest.cacheProvider(seepackages/cloudflare/src/worker.ts). The PR description says that surface is present in Astro 6.0.0 and 6.4.8, but this peer range still allows>=6.0.0-beta.0— prereleases not covered by that claim. If those versions lackapp.manifest.cacheProvider, scheduled publishing cache invalidation will throw at runtime for consumers on a beta/rc. Tighten the range to a version that is known to expose the cache-provider manifest surface."astro": ">=6.0.0",(If verification shows the surface only shipped in a later 6.x, use that as the lower bound instead.)
There was a problem hiding this comment.
This is a re-review of the Astro 7.3 / adapters upgrade. The approach is still sound: resolving the cache provider from the Astro manifest and caching it per isolate is the right replacement for the removed app.pipeline.getCacheProvider, and the scheduled-handler tests cover the success, absent-provider, and retry paths.
I re-read the changed source, package manifests, the changeset, and the docs route. Two issues surfaced that the previous pass missed:
-
The changeset only lists
@emdash-cms/cloudflare, butpackages/core/src/astro/integration/vite-config.ts(shipped via theemdashpackage's./astroexport) received Astro 7.3-specific optimizer/pre-bundle changes. Becauseemdashand@emdash-cms/cloudflareare in the samefixedgroup in.changeset/config.json, the cloudflare entry will driveemdash's version bump without describing the core integration changes. A separate core changeset is needed. -
docs/src/pages/llms.txt.tsassumesconfig.titleis a locale map. The docs config uses a plain string title (title: "EmDash"), so unless Starlight normalizes it to an object at runtime the rendered heading becomes# E. The code should handle a string title explicitly.
Everything else looks consistent: peer/dev dependency ranges match the PR description, the worker singletons follow the globalThis + Symbol.for rule, and the test infrastructure cleanly centralizes Vitest env stripping.
| @@ -0,0 +1,11 @@ | |||
| --- | |||
| "@emdash-cms/cloudflare": minor | |||
There was a problem hiding this comment.
[needs fixing] The frontmatter only covers @emdash-cms/cloudflare, but this PR also changes packages/core/src/astro/integration/vite-config.ts — code shipped through the emdash package's ./astro export. The new optimizer entries, shim aliases, and Astro 7.3 logger handling are observable to emdash consumers and need their own release note. Because the packages are in the same fixed group in .changeset/config.json, the cloudflare entry will bump emdash without describing the core integration changes. Add a separate changeset for emdash describing the Astro 7.3 dev/build compatibility fixes, or restructure the prose so both packages are accurately represented.
For example, add a second file:
| "@emdash-cms/cloudflare": minor | |
| --- | |
| "emdash": patch | |
| --- | |
| Updates the EmDash Astro integration's Vite optimizer configuration for Astro 7.3 compatibility, including pre-bundled entries for new Astro internal modules and the `use-sync-external-store` shim aliases. |
| @@ -18,10 +19,7 @@ export const GET: APIRoute = async ({ site }) => { | |||
|
|
|||
| const lang = config.defaultLocale.lang; | |||
| const title = (lang ? config.title[lang] : undefined) ?? Object.values(config.title)[0] ?? ""; | |||
There was a problem hiding this comment.
[suggestion] docs/astro.config.mjs uses a plain string title: "EmDash", but this line treats config.title as a locale map and falls back to Object.values(config.title)[0]. If Starlight does not normalize a string title into an object at runtime, the fallback will produce the first character (# E). Handle the string case explicitly:
| const title = (lang ? config.title[lang] : undefined) ?? Object.values(config.title)[0] ?? ""; | |
| const title = | |
| typeof config.title === "string" | |
| ? config.title | |
| : (lang ? config.title[lang] : undefined) ?? Object.values(config.title)[0] ?? ""; |
There was a problem hiding this comment.
Re-review: Astro 7.3 / adapters upgrade
The approach is still sound: resolving the cache provider from the Astro app manifest and caching the resulting provider per-isolate is the right replacement for the removed app.pipeline.getCacheProvider, and the worker scheduled tests cover the success, absent-provider, and retry paths.
Both findings from the previous review are addressed:
- Core changeset added.
.changeset/cold-cloudflare-dev.mdnow covers theemdashpackage (which shipspackages/core/src/astro/integration/vite-config.tsvia the./astroexport). Its description focuses on the user-visible outcome—Cloudflare-backed Astro dev servers no longer hang during initial setup/first admin request—rather than listing internal optimizer entries. That's the right shape for a patch changeset. docs/src/pages/llms.txt.tsnow handles string titles. Thetypeof config.title === "string"branch correctly deals with Starlight configs that use a plain string title, and the tagline is preserved in the generatedllms.txtwithout relying onconfig.tagline.
I re-read the changed source, package manifests, the two changesets, the docs route, and the test infrastructure. The new consumerEnvironment helper cleanly centralizes Vitest env stripping, the worker singletons follow the globalThis + Symbol.for rule, the explicit astro/@astrojs/react dev dependencies line up with the PR's goal of testing against the catalog version without forcing it on consumers, and the peer dependency narrowing for @emdash-cms/cloudflare is documented with migration guidance.
No new blockers or regressions surfaced.
There was a problem hiding this comment.
Re-review of the Astro 7.3 / adapters upgrade. The approach remains the right one: Astro 7.3 removed app.pipeline.getCacheProvider, so resolving the configured cache provider from the public app manifest and caching the resulting provider per-isolate via globalThis + Symbol.for is the idiomatic replacement and follows the repository’s singleton discipline.
Both findings from the previous review are addressed:
.changeset/cold-cloudflare-dev.mdnow covers theemdashpackage (the dev-hang fix lives inpackages/core/src/astro/integration/vite-config.ts) and focuses on the user-visible outcome.docs/src/pages/llms.txt.tsnow safely handles stringconfig.titlevalues and keeps the tagline inllms.txtvia a hardcoded constant.
I re-read the changed source, updated package manifests, the two new changesets, the docs route, and the test infrastructure. The new consumerEnvironment helper cleanly centralises Vitest env stripping, the worker singletons follow the required globalThis pattern, the explicit astro/@astrojs/react dev dependencies line up with the goal of testing against the catalog version without forcing it on consumers, and the @emdash-cms/cloudflare peer narrowing is documented with migration guidance. The regression tests cover scheduled cache invalidation success, absent-provider, and retry paths.
No new logic bugs, security issues, AGENTS.md violations, missing locale filters, or logged-out query regressions surfaced. The changesets are user-facing and proportional to the changes. I’m happy to approve.
There was a problem hiding this comment.
Re-review after my previous APPROVED review. The two prior findings are addressed:
.changeset/cold-cloudflare-dev.mdnow targets theemdashpackage and describes the user-facing dev-server hang fix.docs/src/pages/llms.txt.tsnow safely handles a plain-stringconfig.titleand emits the tagline from a hardcoded constant.
I re-read the changed source, updated manifests, the two new changesets, the docs route, the test infrastructure, and the lockfile snapshots. The Astro 7.3 migration path remains sound: resolving the configured cache provider from the public Astro app manifest and caching the app/provider singletons on globalThis via Symbol.for follows the repository’s isolate-singleton discipline, and the regression tests cover success, absent-provider, and retry paths. The consumerEnvironment helper is a clean centralization for spawning Astro subprocesses without the Vitest env vars that trigger background detaching.
No new logic bugs, security issues, AGENTS.md convention violations, missing locale filters, logged-out query regressions, or test-quality problems surfaced. The changesets are user-facing, proportional, and include migration guidance for the peer-dependency narrowing. Approving.
There was a problem hiding this comment.
This is a coherent, well-scoped dependency chore. The Astro 7.3 upgrade is handled at the right layer of abstraction: the Cloudflare Worker cache-invalidation path adapts to Astro 7.3's removal of app.pipeline.getCacheProvider() by resolving the provider from the public app manifest and caching the app/provider singletons on globalThis, and the Vite configuration is updated to pre-bundle the lazy-loaded AT Protocol / auth modules that Astro 7's dependency scanner misses. The approach fits EmDash's isolate-singleton and explicit-catalog-versioning patterns.
I re-checked the whole diff, the previous review's two findings, and the AGENTS.md conventions:
.changeset/cold-cloudflare-dev.mdnow targets the publishedemdashand@emdash-cms/cloudflarepackages and describes the dev-server hang fix in user-facing terms.docs/src/pages/llms.txt.tsnow safely handles a plain-stringconfig.titleand emits the tagline from a hardcodedTAGLINEconstant.- The new
consumerEnvironmenthelper is a clean, centralized way to stripVITEST*env vars from Astro child processes soastro devdoes not detach under Vitest. - The added
worker-scheduled.test.tscases cover the success, absent-provider, and retry paths for scheduled cache invalidation. - No logged-out hot-path queries were added;
docs/src/pages/llms.txt.tsis build-time prerendered and the Worker scheduled handler is not a request path. - No Lingui / RTL / SQL / API-envelope / locale-filter issues are introduced.
- The changesets are proportional, lead with observable behavior, and the minor changeset includes a clear migration note for the Astro 6 prerelease peer-dependency narrowing.
I did not run tooling; this is static analysis only. The author reports full build, typecheck, lint, format, demo, and smoke validation. No logic bugs, regressions, security issues, AGENTS.md violations, or missing test coverage surfaced on this pass.
What does this PR do?
Updates every workspace Astro consumer to Astro 7.3.2 and the latest stable Cloudflare, Node, and React integrations. It also updates the docs site to Starlight 0.42.0 and the WordPress theme scaffold to the same Astro generation.
Astro 7.3 removed the app pipeline property used by scheduled cache invalidation, so the Cloudflare worker now resolves the configured cache provider from the public app manifest and keeps it cached per isolate. The regression test verifies that scheduled publishing still invalidates collection and entry tags.
@emdash-cms/cloudflarenow requires stable Astro 6.0.0 or later; Astro 6 prereleases are no longer supported. Packages that compile or test against Astro use the workspace catalog as an explicit dev dependency, so the repository tests Astro 7.3 without forcing that version on stable Astro 6 consumers.The Wrangler and Workers types catalogs accept compatible releases from 4.125.0 and 5.20260820.1 onward. The lockfile uses those adapter-compatible versions so this PR does not also introduce a later workerd/Miniflare runtime update.
Closes # N/A
Type of change
Checklist
pnpm typecheckpassespnpm lintpassespnpm testpasses (or targeted tests for my change)pnpm formathas been runmessages.pochanges except in translation PRs — a workflow extracts catalogs on merge tomain.The i18n, Discussion, and screenshot items are not applicable: this changes no admin UI strings, adds no feature, and changes no rendered interface.
AI-generated code disclosure
Screenshots / test output
Not applicable; there is no UI change.
Validated with:
pnpm install --frozen-lockfilepnpm buildpnpm typecheckpnpm typecheck:demospnpm typecheck:templatespnpm lintpnpm format:checkpnpm --dir docs build