Skip to content

[personal-wp] Add update flow guard for PWA#3802

Open
fellyph wants to merge 8 commits into
trunkfrom
codex/personal-wp-update-guard
Open

[personal-wp] Add update flow guard for PWA#3802
fellyph wants to merge 8 commits into
trunkfrom
codex/personal-wp-update-guard

Conversation

@fellyph

@fellyph fellyph commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

What changed

  • Adds a generated app-version.json artifact for Personal WP production builds using the bundled build version.
  • Adds an early Personal WP update guard that checks the deployed version before booting the WordPress iframe, blocks stale initial loads, and shows a compact update notice for mid-session changes.
  • Adds update application behavior that asks the service worker registration to update, waits for controllerchange when available, and reloads once.
  • Hardens the Update now flow so the reload fallback timeout starts before service-worker getRegistration(), ready, or update() promises can hang.
  • Uses BroadcastChannel to propagate update availability across open Personal WP tabs.
  • Uses @wordpress/components buttons for the compact update toast actions.
  • Marks app-version.json as no-store in Personal WP .htaccess and website deployment redirects, and excludes it from offline/service-worker caching.
  • Deduplicates offline asset URLs before Cache.addAll() and in the generated offline manifest so duplicate entries such as /sw.js cannot crash offline cache population.
  • Adds Personal WP app-update E2E coverage for stale initial load blocking, mid-session notice dismissal, Update now reload, offline/version-check failure, and two-tab update broadcast.

Scope note

The update guard and app-version.json endpoint are specific to Personal WP / my.wordpress.net.

The offline-cache deduplication uses shared playground-remote service worker code and the shared offline-manifest Vite helper. That means it also affects the website package used for playground.wordpress.net, but only as a narrow bug fix: duplicate offline asset URLs are collapsed before caching. It does not change which unique assets are cached, routing, domains, or website app-shell behavior.

Why

Users with an installed Personal WP PWA can otherwise keep running an old app shell against newer deployed Playground assets. That mismatch can crash startup flows. The version endpoint lets the app detect the mismatch before WordPress boots and prompt for an update instead.

This also avoids the observed Cache.addAll(): duplicate requests failure when an offline manifest contains duplicate asset URLs such as /sw.js.

Validation

  • npm exec nx test playground-personal-wp -- --runInBand
  • npm exec nx test playground-personal-wp
  • npm exec nx test playground-personal-wp --testFile=apply-update.spec.ts
  • npm exec nx typecheck playground-personal-wp
  • npm exec nx lint playground-personal-wp
  • npm exec nx run playground-personal-wp:e2e:playwright
  • npm exec nx run playground-personal-wp:e2e:playwright -- app-update.spec.ts
  • npm exec nx test playground-remote --testFile=offline-mode-cache.spec.ts
  • npm exec nx typecheck playground-remote
  • npm exec nx lint playground-remote
  • npm exec nx build playground-personal-wp
  • php packages/playground/website-deployment/tests.php
  • git diff --check
  • Verified generated dist/packages/playground/my-wordpress-net/app-version.json has schema personal-wp-app-version/v1 and is not listed in assets-required-for-offline-mode.json.
  • Verified generated dist/packages/playground/personal-wp/assets-required-for-offline-mode.json and dist/packages/playground/my-wordpress-net/assets-required-for-offline-mode.json have no duplicate URLs and include /sw.js exactly once.

@akirk

akirk commented Jun 16, 2026

Copy link
Copy Markdown
Member

Interesting! Did you notice an incompatibility (= broken boot?) yourself?

@fellyph

fellyph commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator Author

Updated screenshots of the Personal WP update notification states captured from the local build. The toast actions now use @wordpress/components buttons.

Desktop toast

Personal WP update toast on desktop

Mobile toast

Personal WP update toast on mobile

Initial stale-load update screen

Personal WP update required screen

@akirk

akirk commented Jun 17, 2026

Copy link
Copy Markdown
Member

This is awesome, thanks for flagging and fixing! Is there a reason why you would not want to do the update? I had assumed then it would not boot at all. Thus we could just forgo showing a toast with a decision and just do the update?

@fellyph

fellyph commented Jun 17, 2026

Copy link
Copy Markdown
Collaborator Author

There are a few scenarios:

  • The user was offline and got back only in the meantime; the app was updated, and it got a notification.
  • Due to the cache strategy, the app wasn't updated, and the user gets notified to update the app
  • The app is updated during the user session, and the user will get a notification as well(I need to double-check this scenario)

@fellyph fellyph changed the title [codex] Add Personal WP update guard [personal-wp] Add update flow guard for PWA Jun 17, 2026
@fellyph fellyph marked this pull request as ready for review June 17, 2026 14:24
@fellyph fellyph requested review from a team, JanJakes and Copilot June 17, 2026 14:24

Copilot AI 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.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds a Personal WP–specific PWA update/versioning flow to prevent stale app shells from booting against newer deployed assets, and fixes offline caching crashes caused by duplicate manifest URLs.

Changes:

  • Generate and serve a no-store app-version.json artifact and exclude it from offline/service-worker caching.
  • Add a Personal WP update gate/notice flow with service worker update + cross-tab update propagation via BroadcastChannel.
  • Deduplicate offline manifest URLs before caching and add unit/E2E coverage for update + offline behaviors.

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/vite-extensions/vite-list-assets-required-for-offline-mode.ts Excludes app-version.json from offline manifest and deduplicates listed asset URLs.
packages/vite-extensions/vite-build-version.ts Exposes reusable build-version getter used by both virtual module and build artifact generation.
packages/playground/website-deployment/tests.php Adds regression test asserting app-version.json is not edge/browser cached.
packages/playground/website-deployment/custom-redirects-lib.php Applies no-store headers for app-version.json responses.
packages/playground/remote/src/lib/offline-mode-cache.ts Deduplicates URLs before Cache.addAll() and excludes app-version.json from caching decisions.
packages/playground/remote/src/lib/offline-mode-cache.spec.ts Tests URL deduplication to prevent Cache.addAll() duplicate-request failures.
packages/playground/personal-wp/vite.config.ts Emits app-version.json during build using the shared build version source.
packages/playground/personal-wp/src/main.tsx Wraps the app in an update provider + gate and mounts the update notice UI.
packages/playground/personal-wp/src/lib/pwa-update/use-app-update.tsx Implements periodic/focus/visibility update checks + required/notice state and broadcast integration.
packages/playground/personal-wp/src/lib/pwa-update/update-channel.ts Adds BroadcastChannel helpers to propagate update availability across tabs.
packages/playground/personal-wp/src/lib/pwa-update/update-channel.spec.ts Unit tests for update broadcast and sender self-filtering behavior.
packages/playground/personal-wp/src/lib/pwa-update/apply-update.ts Applies updates via SW registration update + controllerchange race + reload fallback.
packages/playground/personal-wp/src/lib/pwa-update/apply-update.spec.ts Unit tests covering controllerchange, update failures, and hanging SW promises/timeouts.
packages/playground/personal-wp/src/lib/pwa-update/app-version.ts Fetches/validates app-version.json with cache-busting + timeout and reports update availability.
packages/playground/personal-wp/src/lib/pwa-update/app-version.spec.ts Unit tests for current/update/invalid/failed/not-ok version check outcomes.
packages/playground/personal-wp/src/components/app-update-notice/style.module.css Styles for the required update screen, loading gate, and compact toast notice.
packages/playground/personal-wp/src/components/app-update-notice/index.tsx Renders the gate, toast notice, loading screen, and required update UI.
packages/playground/personal-wp/playwright/e2e/app-update.spec.ts E2E coverage for stale-load blocking, notice dismissal, reload update, failure fallback, and two-tab broadcast.
packages/playground/personal-wp/.htaccess Ensures app-version.json is served with no-store headers.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +60 to +66
const updateAndWaitForController = (async () => {
await updateServiceWorker(serviceWorker);
await controllerChanged;
})();
await Promise.race([updateAndWaitForController, delay(timeoutMs)]);
removeControllerChangeListener();
reloadOnce();
Comment on lines +29 to +34
<div className={css.notice} role="status" aria-live="polite">
<div className={css.noticeContent}>
<strong>Update available</strong>
<span>Restart My WordPress to load the latest version.</span>
</div>
<div className={css.noticeActions}>
Comment on lines +117 to +122
const files = Array.from(
new Set(
distDirectoriesToList.flatMap((dir) => {
const absoluteDirPath = join(outputDir, dir);
console.log(`Listing files in ${absoluteDirPath}`);
return listFiles(absoluteDirPath)
Comment on lines 135 to +137
})
.filter((item) => {
return !patternsToNotCache.some((pattern) => {
if (pattern instanceof RegExp) {
return pattern.test(item);
}
return pattern === item;
});
});
});
)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants