diff --git a/functionalities.php b/functionalities.php index c1ef07c..43ae96b 100644 --- a/functionalities.php +++ b/functionalities.php @@ -73,6 +73,7 @@ function () { ); \add_action( 'updated_option', array( '\Functionalities\Core\Module_Registry', 'handle_option_update' ), 10, 3 ); +\add_action( 'added_option', array( '\Functionalities\Core\Module_Registry', 'handle_option_add' ), 10, 2 ); // Activation hook. \register_activation_hook( diff --git a/includes/core/class-module-registry.php b/includes/core/class-module-registry.php index 2ad4e70..5417892 100644 --- a/includes/core/class-module-registry.php +++ b/includes/core/class-module-registry.php @@ -152,6 +152,20 @@ public static function handle_option_update( string $option, $old_value, $value } } + /** + * React when settings are saved for the first time. + * + * WordPress fires added_option instead of updated_option when an option did + * not previously exist, which is the normal fresh-install PWA path. + * + * @param string $option Option name. + * @param mixed $value New value. + * @return void + */ + public static function handle_option_add( string $option, $value ): void { + self::handle_option_update( $option, null, $value ); + } + /** * Build one normalized definition. * diff --git a/includes/features/class-pwa.php b/includes/features/class-pwa.php index 5e8a61c..497ac1c 100644 --- a/includes/features/class-pwa.php +++ b/includes/features/class-pwa.php @@ -846,7 +846,9 @@ private static function output_offline_page(): void { ob_end_clean(); } - \status_header( 503 ); + // This application shell is fetched during service-worker installation. + // cache.addAll() rejects non-2xx responses, so it must be cacheable. + \status_header( 200 ); \nocache_headers(); ?> diff --git a/readme.txt b/readme.txt index fda8dff..128741c 100644 --- a/readme.txt +++ b/readme.txt @@ -135,6 +135,7 @@ Before uninstalling, go to the Functionalities dashboard and check **"Delete all == Changelog == = 1.4.8 = +* Fixed: Fresh PWA settings now register rewrite endpoints immediately, and the offline application shell returns a cacheable success response so service-worker precaching can complete. * Added: True lazy module registry. A frontend request with all modules disabled loads no feature class files; enabling one module loads only that feature and shared dependencies. * Added: Versioned settings export/import with dry-run differences, module validation, default custom-code redaction, and an explicit code opt-in. * Added: Privacy-conscious diagnostics download with software versions, enabled modules, writable-path status, and rewrite-rule health. Task content, redirects, users, secrets, and site URLs are excluded. diff --git a/tests/ModuleRegistryTest.php b/tests/ModuleRegistryTest.php index 34d5e40..8ea6c72 100644 --- a/tests/ModuleRegistryTest.php +++ b/tests/ModuleRegistryTest.php @@ -40,6 +40,8 @@ public function test_pwa_boots_safely_before_wordpress_init(): void { $this->assertSame( array( 'class-pwa.php' ), $result['features'] ); $this->assertContains( 'init', $result['hooks'] ); + $this->assertContains( 'added_option', $result['hooks'] ); + $this->assertContains( 'updated_option', $result['hooks'] ); } /** diff --git a/tests/PwaOfflineStatusTest.php b/tests/PwaOfflineStatusTest.php new file mode 100644 index 0000000..806e833 --- /dev/null +++ b/tests/PwaOfflineStatusTest.php @@ -0,0 +1,26 @@ +assertIsString( $source ); + $this->assertMatchesRegularExpression( + '/private static function output_offline_page\(\).*?status_header\( 200 \)/s', + $source + ); + $this->assertStringContainsString( 'c.addAll(PRECACHE_URLS)', $source ); + } +}