@pwa-platform/entry-resilience 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +68 -42
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -1,87 +1,113 @@
1
1
  # @pwa-platform/entry-resilience
2
2
 
3
- Optional entry recovery for a PWA whose original address is moving or unavailable. Install it alongside
4
- `@pwa-platform/vite`; use the same `PwaIdentity` object for both Vite plugins. The package supports Vite 5 and 8
5
- on Node 22 or later.
3
+ Optional entry recovery for an installed PWA whose original address is moving or unavailable. The package emits a
4
+ precache-protected recovery page, validates application-supplied entry manifests, stores the newest accepted
5
+ manifest locally and shows a validated alternative URL only when recovery conditions are met. It supports Vite 5
6
+ and Vite 8 on Node.js 22 or later.
6
7
 
7
- The application obtains a manifest through its own request layer and hands the decoded object to the client API.
8
- The platform checks its shape, increasing sequence and expiry, then stores it locally. A recovery page shows a
9
- validated alternative address; navigation happens only after the user clicks it. Neither cookies nor login state
10
- move between origins.
8
+ This is not an automatic redirect, cross-origin session transfer or trust service. Navigation happens only after
9
+ the user clicks the recovery link, and cookies or login state never move between origins.
11
10
 
12
- ## Install and configure
11
+ ## Install and build integration
13
12
 
14
13
  ```sh
15
14
  npm install @pwa-platform/entry-resilience @pwa-platform/vite
16
15
  ```
17
16
 
18
17
  ```ts
19
- // vite.config.ts — reuse the identity already passed to pwa()
18
+ // vite.config.ts — reuse the exact identity object passed to pwa()
20
19
  import { pwa } from "@pwa-platform/vite";
21
20
  import { pwaEntryResilience } from "@pwa-platform/entry-resilience/vite";
22
21
 
23
22
  plugins: [
24
23
  pwa({ identity, policy, install, topology }),
25
- pwaEntryResilience({ identity, maxValidityDays: 30, locale: "zh-CN" }),
24
+ pwaEntryResilience({
25
+ identity,
26
+ maxValidityDays: 30,
27
+ locale: "zh-CN",
28
+ messages: { heading: "应用入口已变更" },
29
+ css: ".pwa-entry { --pwa-entry-accent: #006e52; }",
30
+ }),
26
31
  ]
27
32
  ```
28
33
 
29
- The PWA policy must classify the mount-relative `/pwa-entry.html` as a `cache-first` asset, and its fingerprinted
30
- script under `/assets` as an asset too. The build rejects a recovery page that is not precached. `locale` accepts
31
- `"zh-CN"` or `"en"`; `messages` overrides individual strings. The `css` option appends host CSS after the
32
- default recovery-page style. To change the button, override `--pwa-entry-accent` and
33
- `--pwa-entry-accent-fg` in `.pwa-entry`; see the [integration guide](https://github.com/haigeerlab/pwa-platform/blob/main/docs/guides/entry-recovery-integration.md)
34
- for light/dark theme selectors and strict CSP hashes.
34
+ The PWA policy must classify mount-relative `/pwa-entry.html` as a `cache-first` asset and cover its fingerprinted
35
+ script (normally through `/assets`). The build rejects a recovery page that is not precached. `maxValidityDays`
36
+ accepts 1–90 and defaults to 30. `locale` accepts `"zh-CN"` or `"en"`; `messages` overrides individual strings;
37
+ `css` is appended after the default page style and must not contain a closing `</style` sequence.
35
38
 
36
- ```ts
37
- import { updateEntryManifest, checkEntryRecovery, setPwaTheme } from "@pwa-platform/entry-resilience/client";
39
+ ## Browser integration
40
+
41
+ The application owns fetching, authentication, authorization, decryption and polling. Hand the already-decoded
42
+ object to the package:
38
43
 
39
- const result = await updateEntryManifest(await api.getEntryManifest());
44
+ ```ts
45
+ import {
46
+ checkEntryRecovery,
47
+ setPwaTheme,
48
+ updateEntryManifest,
49
+ } from "@pwa-platform/entry-resilience/client";
50
+
51
+ const response = await fetch("/api/pwa-entry", { credentials: "include" });
52
+ const result = await updateEntryManifest(await response.json());
40
53
  if (!result.accepted) reportDiagnostics(result.diagnostics);
41
54
 
42
55
  const recovery = await checkEntryRecovery({ returnPath: location.pathname });
43
56
  if (recovery.kind === "available") showRecoveryLink(recovery.recoveryPageUrl);
44
57
 
45
- setPwaTheme("system"); // or "light" / "dark"
58
+ setPwaTheme("system"); // "light", "dark" or "system"
46
59
  ```
47
60
 
48
- The application owns the request, authentication, decryption and polling. Keep the manifest endpoint protected:
49
- the platform validates structure and timing, but does not authenticate its source or restrict destination origins.
50
- An ordinary device-wide loss of connectivity with a `normal` manifest does not create a domain-outage suggestion.
61
+ Call `updateEntryManifest()` after a successful application-controlled fetch and at the application's chosen refresh
62
+ interval. `checkEntryRecovery()` probes the current entry and decides whether a recovery suggestion is warranted;
63
+ it does not navigate. `setPwaTheme()` stores a same-origin preference read by the recovery page and silently no-ops
64
+ when storage is unavailable.
51
65
 
52
- ## Validate a manifest before you publish it
66
+ ## Manifest contract and validation
53
67
 
54
- Use the same validator in Node before publishing a manifest so an invalid manifest fails CI or the backend job
55
- instead of being silently rejected only by a browser page.
68
+ Validate a manifest in CI or backend code with the side-effect-free root entry:
56
69
 
57
- ```js
70
+ ```ts
58
71
  import { parseEntryManifest } from "@pwa-platform/entry-resilience";
59
72
 
73
+ const now = Date.now();
60
74
  const manifest = {
61
75
  sequence: 7,
62
- expiresAt: "2026-10-01T08:00:00Z",
76
+ expiresAt: new Date(now + 7 * 86_400_000).toISOString().replace(/\.\d{3}Z$/, "Z"),
63
77
  status: "migrating",
64
- reason: { code: "planned-migration", message: "Domain retires October 1." },
78
+ reason: { code: "planned-migration", message: "The old domain is retiring." },
65
79
  entries: [{ origin: "https://new.example.com", startPath: "/app/" }],
66
80
  };
67
81
 
68
82
  const result = parseEntryManifest(manifest, {
69
- appId: "pwaexample",
83
+ appId: "exampleapp",
70
84
  environment: "production",
71
- // Match the maxValidityDays in this app's Vite config.
72
85
  maxValidityDays: 30,
73
- // Inject the moment at which validity should be judged.
74
- now: Date.now(),
86
+ now,
75
87
  });
76
88
 
77
- if (!result.ok) {
78
- for (const { code, path } of result.diagnostics) {
79
- console.error(`${path || "(manifest)"}: ${code}`);
80
- }
81
- process.exit(1);
82
- }
89
+ if (!result.ok) throw new Error(result.diagnostics.map((item) => item.code).join(", "));
83
90
  ```
84
91
 
85
- `parseEntryManifest` never throws or reads ambient state. It is the same shape validator used by
86
- `updateEntryManifest`; expiry and sequence are checked again when the browser accepts the manifest. Use an
87
- `expiresAt` value in `YYYY-MM-DDTHH:mm:ssZ` format without milliseconds.
92
+ Use UTC `YYYY-MM-DDTHH:mm:ssZ` timestamps without milliseconds. Browsers additionally reject expired manifests and
93
+ sequences that do not advance beyond the locally accepted value.
94
+
95
+ ## Export map
96
+
97
+ | Entry | Intended use |
98
+ | --- | --- |
99
+ | `@pwa-platform/entry-resilience` | Manifest types/diagnostics, `parseEntryManifest`, pure recovery orchestration and advanced browser ports. |
100
+ | `@pwa-platform/entry-resilience/vite` | `pwaEntryResilience()` and build option/page message types. |
101
+ | `@pwa-platform/entry-resilience/client` | `updateEntryManifest()`, `checkEntryRecovery()` and `setPwaTheme()`. |
102
+
103
+ ## Security boundary
104
+
105
+ - The package validates schema, sequence and expiry; it does not authenticate the manifest source or restrict the
106
+ destination origins. Your backend and request layer are the trust boundary.
107
+ - Never expose a debugging hook that lets arbitrary page visitors call `updateEntryManifest()` with their own data.
108
+ - The recovery page displays a destination before a user click. It does not auto-redirect or transfer credentials.
109
+ - A normal manifest plus device-wide loss of connectivity does not create a domain-outage suggestion.
110
+ - Keep `identity` identical across both Vite plugins and treat identity changes as a migration.
111
+
112
+ See the complete [entry-recovery integration guide](https://github.com/haigeerlab/pwa-platform/blob/main/docs/guides/entry-recovery-integration.md)
113
+ and [entry-resilience specification](https://github.com/haigeerlab/pwa-platform/blob/main/spec/pwa-entry-resilience.md).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pwa-platform/entry-resilience",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Validated fallback entries for installed PWA applications.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  "dist"
25
25
  ],
26
26
  "dependencies": {
27
- "@pwa-platform/vite": "0.1.0"
27
+ "@pwa-platform/vite": "0.2.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "vite": "^5.0.0 || ^8.0.0"
@@ -33,8 +33,8 @@
33
33
  "@playwright/test": "1.63.0",
34
34
  "@types/node": "24.13.4",
35
35
  "vite": "8.3.0",
36
- "@pwa-platform/contracts": "0.1.0",
37
- "@pwa-platform/browser-test-harness": "0.0.0"
36
+ "@pwa-platform/browser-test-harness": "0.0.0",
37
+ "@pwa-platform/contracts": "0.2.0"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=22.0.0"