Skip to content

Chore/cloudflare - #1502

Merged
IMax153 merged 47 commits into
mainfrom
chore/cloudflare
Sep 4, 2026
Merged

Chore/cloudflare#1502
IMax153 merged 47 commits into
mainfrom
chore/cloudflare

Conversation

@IMax153

@IMax153 IMax153 commented Aug 19, 2026

Copy link
Copy Markdown
Member

Type

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update

Description

Migrates the Effect Website from Vercel to Cloudflare

Related

  • Related Issue #
  • Closes #

@github-actions

github-actions Bot commented Aug 21, 2026

Copy link
Copy Markdown

Cloudflare Preview

URL: https://effectwebsite-websiteworker-pr-1502-yu4eeuc5au3ma34a.matechs.workers.dev

Built from commit 8de80c6.


This comment updates automatically with each deployment.

@IMax153
IMax153 marked this pull request as ready for review August 21, 2026 19:32
@IMax153
IMax153 enabled auto-merge (squash) August 21, 2026 19:32
@IMax153
IMax153 disabled auto-merge August 31, 2026 18:27
IMax153 and others added 22 commits September 4, 2026 10:10
Turn on Astro 7.2's experimental `incrementalBuild` and give every
prerendered route a cache key, so a build re-renders only the pages whose
data or code actually changed. A fully cached build drops from 1m33s to
25s, and the remainder is Vite bundling rather than page rendering.

Astro disables the cache when `build.concurrency > 1`, so concurrency
goes to 1. That costs ~11s on a cold build (1m22s to 1m33s), which the
warm case pays back many times over. `cacheDir` moves out of
`node_modules` to `apps/web/.astro-cache` so CI can restore and save it
without caching the dependency tree.

Two things blocked the API reference from ever hitting the cache. Every
module page baked the global Effect git revision into its `og:image` URL,
and the content loader mixes the same revision into `entry.digest` — both
change on every snapshot, so all 1,090 pages changed even when their
module did not. Pages now use their own `reflectionDigest`, which busts a
module's social card exactly when that module's docs change.

Those pages never call `render()`, so Astro's automatic content-entry
tracking does not cover them and the cache key is the only correctness
mechanism. Keying on the module alone would be wrong: each page also
renders a package-scoped sidebar, a version-wide package switcher,
version-wide `module:Foo` link resolution, and a v3/v4 switcher. The
digests in features/api-reference/cache-key.ts cover each of those and
are derived from metadata only, which moves far less often than
reflection content.

Verified a fully cached build is byte-identical to a full render across
1,793 files, and that the existing link verifier passes against the
restored HTML. Targeted checks: one blog post body invalidates 1 page,
one docs sidebar label invalidates the 110 pages of that version and no
v3 page, one reflection invalidates 1 of 1,090 API pages, and editing
DocShell.astro invalidates docs and API pages but no blog post.

`astro build --force` is unreachable because alchemy invokes the build
itself, so the escape hatch is discarding the cache directory, exposed as
a `force_full_build` input on the production workflow.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEsRhqRu7xjNbZNjAm1vbj
A preview stage that sits idle longer than its store's TTL cannot deploy:
`SynchronizePreviewSearch` fails with `404 Stores with identifiers '...'
not found`, taking `WebsiteWorker` and the preview comment down with it.

Preview stores are created with `expiresAfter: { anchor: "last_active_at",
days: 7 }`, so Mixedbread deletes them on its own while alchemy state
keeps holding the id. `reconcile` already recovers from a missing store,
but it never ran, because `diff` decided purely from input equality — and
the inputs of an untouched PR do not change. The store noop'd and the
following sync inherited a dead id.

`diff` now confirms the store still exists before returning noop, and
routes to `reconcile` when it is gone. Non-404 failures still surface
rather than being swallowed, matching `read` and `reconcile`.

This only shows up on a PR that both predates the TTL and touches a path
`SearchChanges` watches, which is why it stayed latent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEsRhqRu7xjNbZNjAm1vbj
The previous commit made `diff` notice an expired store, but the deploy
still failed on the same dead id: alchemy resolves attributes listed in
`provider.stables` straight from previous state, without waiting for
`reconcile`. `SynchronizePreviewSearch` therefore started 100ms before
`PreviewSearchStore` finished updating and inherited the id of the store
that had just been replaced.

`id` is only stable when the store is updated in place. When it has to be
recreated the replacement gets a new id, so the diff now returns an empty
`stables`, which Plan treats as authoritative over the provider list and
holds dependents until the real output lands.

Move the existence check ahead of the input comparison too, so a run that
changes props against a store that has already expired takes the same
path rather than reporting `id` as stable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEsRhqRu7xjNbZNjAm1vbj
Moving `cacheDir` out of `node_modules` relocated the content layer's
`data-store.json`, but `openGraphMetadataPlugin` still resolved the old
default, so the build died bundling the SSR open-graph route:

  NotFound: FileSystem.readFile (apps/web/node_modules/.astro/data-store.json)

The config now derives `cacheDir` once and hands the same URL to both
astro and the plugin, so the two cannot drift again.

This only reproduced on a clean checkout: any machine that had built
before the move still had a stale store at the old path, which is exactly
where it was being read from.

Cover it with a test that drives the plugin's load hook against a
temporary cacheDir, since neither of the existing gates would have caught
it — `astro check` does not bundle, and the SSR route that pulls in the
virtual module is only reachable through a real adapter.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TEsRhqRu7xjNbZNjAm1vbj
@IMax153
IMax153 enabled auto-merge (squash) September 4, 2026 14:31
@IMax153
IMax153 disabled auto-merge September 4, 2026 14:31
@IMax153
IMax153 merged commit 9f5728f into main Sep 4, 2026
4 checks passed
@IMax153
IMax153 deleted the chore/cloudflare branch September 4, 2026 14:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant