Files
suaveplan/openspec/archive/2026-05-25-enterprise-pwa/proposal.md
2026-05-27 23:29:53 +01:00

88 lines
5.2 KiB
Markdown

# Proposal: Enterprise migration of `@suaveplan/pwa`
- **Status**: PROPOSED
- **Package**: `@suaveplan/pwa`
- **Tier**: browser
- **Category / plugin**: `foundation / data`
- **Source**: upstream `packages/browser/pwa` (1.0.4)
- **Design**: ./design.md
- **Spec delta**: ./specs/pwa/spec.md
## Summary
Migrate `@suaveplan/pwa` from `../suaveplan/packages/browser/pwa` (1.0.4) to genesis at
`packages/foundation/data/pwa/` as version `0.1.0`. The upstream package ships four
managers (`ServiceWorkerManager`, `SyncQueue`, `PushNotificationManager`,
`InstallPromptManager`) but relies heavily on `vi.stubGlobal` in tests (banned by
CLAUDE rule 2), stores the sync queue in `localStorage`, has no Zod boundaries, no
OTel, no typed errors, and no caching-strategy abstraction or manifest builder.
The genesis rewrite ships the full contract:
- **`ServiceWorkerManager`** — registration, activation, update polling, `skipWaiting`,
state-change listeners, injectable `ServiceWorkerContainer` seam.
- **`SyncQueue`** — pluggable persistence (`MemoryStore`, `IndexedDbStore`,
`LocalStorageStore`), Zod-validated entries, exponential-backoff retry,
`BackgroundSyncBridge` for `SyncManager.register()` with tab-visibility fallback.
- **`PushNotificationManager`** — permission + `PushManager` subscribe / unsubscribe,
VAPID `applicationServerKey` decoding, convenience notification dispatch through
the active registration.
- **`InstallPromptManager`** — `beforeinstallprompt` capture, programmatic `prompt()`,
`appinstalled` integration, dismiss persistence.
- **`CachingStrategies`** — `cacheFirst`, `networkFirst`, `staleWhileRevalidate`,
`networkOnly`, `cacheOnly` route-handlers + `registerRoute(pattern, strategy)`
router suitable for use inside a SW global scope.
- **`ManifestBuilder`** — fluent builder for the Web App Manifest (name, icons,
shortcuts, related apps, share target, display, theme, orientation, scope) with
Zod validation and `toBlob()` for inline registration.
- **`UpdateController`** — coordinates `skipWaiting` + `controllerchange` reload across
tabs via `BroadcastChannel`, exposes "update available" event.
- **`VersionMigration`** — schema-versioned queue migration so older payloads in
storage are upgraded on `SyncQueue` load.
- **`PwaObservability`** — OTel counters + histograms covering SW install / activate /
update, sync queue flush, push subscriptions, install-prompt acceptance.
- **`PwaError` + `PWA_CODES`** — typed errors with metadata for HTTP status,
severity, retryability, description.
## Parity Checklist
| Capability | Upstream | Migrated | Notes |
|---|---|---|---|
| `ServiceWorkerManager` (register / unregister / checkForUpdate / skipWaiting / state / onStateChange) | OK | OK | Plus injectable container seam, typed error on failure. |
| `SyncQueue` (enqueue / remove / getAll / size / clear / process) | OK | OK | Pluggable store; default switched to IndexedDB; localStorage still available as a `LocalStorageStore`. |
| `PushNotificationManager` (isSupported / getPermission / requestPermission / subscribe / unsubscribe / getSubscription) | OK | OK | Same surface; VAPID decoding extracted into a helper. |
| `InstallPromptManager` (listen / unlisten / prompt / dismiss / clearDismissed / state / onStateChange / reset) | OK | OK | Plus persisted dismissal across sessions. |
| Constants (`DEFAULT_*`, `SYNC_QUEUE_STORAGE_KEY`, `INSTALL_PROMPT_KEY`) | OK | OK | Preserved + extended (`DEFAULT_RETRY_BACKOFF_FACTOR`). |
| Subpath exports per module | OK | OK | Expanded per new modules. |
| Caching strategies (`cacheFirst`, `networkFirst`, `staleWhileRevalidate`, …) | none | OK | New. |
| Route router (`registerRoute`) | none | OK | New. |
| Web App Manifest builder | none | OK | New. |
| Update controller w/ cross-tab reload coordination | none | OK | New. |
| Background-sync bridge (`SyncManager.register()`) | none | OK | New, with visibility-change fallback. |
| Version migration on stored queue payloads | none | OK | New, schema bump from v1 → vN. |
| Zod validation on `SyncQueueEntry` | none | OK | New. |
| OTel counters + histograms | none | OK | New. |
| `PWA_CODES` + `PwaError` | none | OK | New 8-code registry. |
| `vi.stubGlobal` in tests | yes | none | Replaced with happy-dom + in-memory seams. |
| `// @ts-expect-error` in tests | 4 | 0 | Removed; edge cases modelled as typed branches. |
## Impact
### Affected dependents
No existing genesis package depends on `@suaveplan/pwa` today. Future consumers (the
suaveplan webapp shell + service-worker bundle) will pick up the new subpath exports.
### Risks / mitigations
- **IndexedDB on WSL2 + happy-dom** — happy-dom ships `fake-indexeddb`-compatible
IDB. We seam `IDBFactory` so unit tests run against a deterministic in-memory store
injected by the test, and only the IDB-store-specific integration tests touch
happy-dom's IDB.
- **`PushManager` / `Notification` absent in non-secure contexts** — every entry
point guards with feature detection and falls back to a typed `PWA.PUSH_UNSUPPORTED`
error; the test seam supplies a real-shaped `PushManager` stand-in.
- **BroadcastChannel polyfill** — happy-dom exposes a real `BroadcastChannel`, so the
`UpdateController` cross-tab coordination is exercised in unit tests against the
same primitive production uses.