| 1 | --- |
| 2 | name: rastrillo-pwa |
| 3 | description: Add installation, a public offline fallback and service-worker update handling to a Rastrillo web app, with optional aviso Web Push composition. |
| 4 | --- |
| 5 | |
| 6 | # Rastrillo PWA |
| 7 | |
| 8 | Module and source: `amadan.net/rastrillo/pwa`. Read this skill from the |
| 9 | version the app imports. This kit adds installability and an offline |
| 10 | fallback, not offline data or writes. For the latter, read |
| 11 | [docs/offline.md](docs/offline.md) before choosing an app data model. |
| 12 | |
| 13 | Use `examples/basic/main.go` as the complete wiring reference. It is a |
| 14 | separate Go module, so read it from the repository, not a module-cache zip. |
| 15 | |
| 16 | - Create `pwa.Manifest` with stable `ID`, `Name`, `StartURL`, `Scope` and |
| 17 | icons. Paths are root-relative, scope ends in `/`, and start URL stays |
| 18 | within scope. Call `.Handler()` at boot and check the error. Supply actual |
| 19 | 192x192 and 512x512 PNGs, plus a 180px Apple touch icon. Branding is app-owned. |
| 20 | - Mount the manifest at `/manifest.webmanifest`. Mount `pwa.Assets()` using |
| 21 | `http.StripPrefix("/pwa", ...)` at `/pwa/`. Assets have JavaScript MIME |
| 22 | types and `no-cache`; add manifest, theme-colour and Apple icon head tags. |
| 23 | - Serve app-owned `/sw.js` at its intended scope with JavaScript MIME and |
| 24 | `Cache-Control: no-cache`. It imports `/pwa/worker.js` and calls |
| 25 | `RastrilloPWA.install()` once. For `/app/` scope, use `/app/sw.js` and |
| 26 | matching manifest scope/start paths; asset helpers may live elsewhere. |
| 27 | Serve the manifest and worker assets without authentication redirects. |
| 28 | - Import `register` from `/pwa/client.mjs`. It returns a registration or |
| 29 | `null` when unsupported. It reports waiting updates through `onUpdate`; |
| 30 | it never reloads or asks for permission. Without `clients.claim`, the |
| 31 | first page becomes controlled on its next navigation. Wait for |
| 32 | `navigator.serviceWorker.ready` before passing the registration to push. |
| 33 | |
| 34 | Update activation is app policy. Prefer asking people to save and close all |
| 35 | tabs. `activateUpdate(registration)` explicitly activates a waiting worker |
| 36 | and affects every tab in its scope. Call only after resolving unsaved work |
| 37 | across them. The app owns any `controllerchange` reload. |
| 38 | |
| 39 | The worker intercepts only in-scope GET navigations. Network failures get a |
| 40 | 503 public offline page; 401/403/500 responses remain unchanged. API calls |
| 41 | and writes stay on the network. No Cache Storage or IndexedDB writes. |
| 42 | `install({offlineHTML})` accepts a trusted, public, self-contained document |
| 43 | for custom copy/translations. Its CSP permits inline style, no scripts or |
| 44 | external assets. Never interpolate a signed-in person's data into it. |
| 45 | |
| 46 | For push, load the pinned `amadan.net/rastrillo/aviso` skill and compose its |
| 47 | worker handlers into the same `/sw.js`. Keep one registration per scope; |
| 48 | do not register a second push worker over it. Aviso owns subscriptions and |
| 49 | transport. The app owns notification policy, payloads and encryption. |
| 50 | Push permission must be requested from a user gesture. On iOS, guide the |
| 51 | person to install and sign in inside the Home Screen copy first. |
| 52 | |
| 53 | Validate with `make ci` and the app's own gate. The module gate includes its |
| 54 | nested example and Chromium/WebKit worker tests; physical-device install |
| 55 | and push checks are separate. There is no native adapter in this module. |
| 56 | |