88 lines
5.2 KiB
Markdown
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.
|