Skip to content

fix(plugins): an upgrade no longer 404s every published page - #359

Open
mostafasadeghidev wants to merge 1 commit into
CoreBunch:mainfrom
mostafasadeghidev:fix/plugin-upgrade-keeps-published-assets
Open

fix(plugins): an upgrade no longer 404s every published page#359
mostafasadeghidev wants to merge 1 commit into
CoreBunch:mainfrom
mostafasadeghidev:fix/plugin-upgrade-keeps-published-assets

Conversation

@mostafasadeghidev

Copy link
Copy Markdown

The bug

Published HTML links a plugin's frontend assets by version — /uploads/plugins/<id>/<version>/frontend/app.js — because the version is what makes the URL cache-bustable.

The upgrade flow deleted the old version's directory as its last step. Nothing re-renders the artefacts already baked to disk, so every one of them kept pointing at files that were gone.

On a real site an upgrade took out jQuery, GSAP, Lenis, Splide and the boot script across all six pages at once — the entire site's JavaScript. Nothing warned, nothing prompted a re-publish, and the pages still returned 200 with a correct-looking document. Only the browser console showed it.

publishSite.ts describes the baked slot as "a self-contained static export". Plugin frontend assets are the one thing referenced from outside it, which is what made that description untrue.

The change

The delete moves from upgrade to publish. A publish is the only thing that rewrites those URLs, which makes it the exact moment the old files stop being referenced — so sweepStalePluginVersionAssets runs after the slot swap and drops every version directory except the installed one.

Between an upgrade and the next publish both versions sit on disk: the installed one for new renders, the previous one for pages not yet re-baked. The cost is bounded by how many upgrades happen between two publishes, and each version is a bundle rather than a library.

Two deliberate refusals:

  • A plugin with no installed record is never swept. Uninstall already removes its tree, so anything still there is unexplained — and a publish is a bad moment to act on something unexplained.
  • Sweep failure is logged and swallowed. Leftover files are wasted disk, never a broken page, and a publish must not fail over cleanup.

Rollback still deletes the new version's directory, which is correct: no published page has ever referenced it.

Impact

Fixes silent breakage on every site that upgrades a plugin contributing frontend.assets. No schema change. Disk usage between publishes grows by one bundle per upgrade, reclaimed at the next publish.

Verification

bun run build   # tsc -b && vite build — clean
bun test server/__tests__/stale-plugin-assets.test.ts   # 6 pass (real temp dirs)
bun test server/__tests__ src/__tests__/architecture    # same failures as main, none new

Published HTML links a plugin's frontend assets by version —
`/uploads/plugins/<id>/<version>/frontend/app.js` — because the version is
what makes the URL cache-bustable. The upgrade flow deleted the old version's
directory as its last step, and nothing re-rendered the artefacts already on
disk, so every one of them kept pointing at files that were gone.

On a real site an upgrade took out jQuery, GSAP, Lenis, Splide and the boot
script across all six pages at once — the entire site's JavaScript, silently.
Nothing warned, nothing prompted a re-publish, and the pages still returned
200 with a correct-looking document. The fix was one publish, for someone who
already knew that.

So the delete belongs at publish, not at upgrade. A publish is the only thing
that rewrites those URLs, which makes it the exact moment the old files stop
being referenced. `sweepStalePluginVersionAssets` runs after the slot swap and
drops every version directory except the installed one.

Between an upgrade and the next publish both versions sit on disk: the
installed one for new renders, the previous one for pages not yet re-baked.
The cost is bounded by how many upgrades happen between two publishes, and
each version is a bundle rather than a library.

A plugin with no installed record is never swept. Uninstall already removes
its tree, so anything still there is unexplained — and a publish is a bad
moment to act on something unexplained. Sweep failure is logged and swallowed:
leftover files are wasted disk, never a broken page.

Rollback still deletes the NEW version's directory, which is correct — no
published page has ever referenced it.
@mostafasadeghidev
mostafasadeghidev marked this pull request as ready for review August 9, 2026 03:00
mostafasadeghidev added a commit to mostafasadeghidev/Instatic that referenced this pull request Aug 9, 2026
Brings in six upstream commits — collaborative-persistence and deterministic
publishing, HEAD answered like GET on published pages, runtime script
diagnostics, context-menu and data-token polish, the Selectors panel Used
filter, and floating panels clearing docked sidebars.

One of ours is now redundant and is dropped. Upstream's `e9e99dff` solves the
opaque publish failure that CoreBunch#358 solved, and solves it better: its message
carries `path:line:column`, mine carried only the diagnostic text. Its
`RuntimeScriptBuildError` replaces `PublishRuntimeBuildError`, and both of my
files are deleted rather than kept beside it.

Three conflicts, all in the publish path or its tests:

- `publishSite.ts` and `handlers/cms/publish.ts` — the same fix from two
  sides. Took upstream's wholesale and re-applied only the stale-plugin-asset
  sweep (CoreBunch#359), which upstream does not have.
- `tables.test.ts` — not a contested edit at all: both sides had added an
  independent suite to one file (ours `created_by_plugin_id`, theirs
  `route_base`). Split into two describes with their own preamble; all ten
  pass.

Two of our own gates needed the fix, not the exception:

- `siteImport/types.ts` sat 2 lines over the 700-line ceiling because the
  file was already at 696 upstream and CoreBunch#349 added a six-line doc comment.
  Tightened our own prose rather than raising the ceiling or extracting a
  single union member into a module of its own.
- The ContentPage bundle cap is raised 90 → 92 KB on this fork only, with the
  reason recorded in the budget entry: the stack layers twelve pending
  features whose shared imports land in that chunk, and each is inside 90 KB
  on its own branch.

One test asserted the behaviour CoreBunch#359 deliberately changed — that an upgrade
deletes the old version's directory. Updated to assert the new contract, with
the reason, since that assertion is exactly what broke live sites.

Verified: `bun run build` clean, `tsc -b` clean. Full suite compared against a
clean upstream-main worktree — 327 failures there, and after these fixes the
only remaining difference is environment flake (`EBUSY` on the Windows temp-db
teardown, which hits upstream too). Zero architecture gates added. Both
plugins typecheck and pass against the merged engine.
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