@pwa-platform/entry-resilience 0.1.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.
- package/LICENSE +21 -0
- package/README.md +87 -0
- package/dist/browser/index.d.ts +28 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +39 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/indexeddb.d.ts +12 -0
- package/dist/browser/indexeddb.d.ts.map +1 -0
- package/dist/browser/indexeddb.js +61 -0
- package/dist/browser/indexeddb.js.map +1 -0
- package/dist/browser/probes.d.ts +13 -0
- package/dist/browser/probes.d.ts.map +1 -0
- package/dist/browser/probes.js +36 -0
- package/dist/browser/probes.js.map +1 -0
- package/dist/check.d.ts +21 -0
- package/dist/check.d.ts.map +1 -0
- package/dist/check.js +48 -0
- package/dist/check.js.map +1 -0
- package/dist/client/index.d.ts +19 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +49 -0
- package/dist/client/index.js.map +1 -0
- package/dist/decide.d.ts +19 -0
- package/dist/decide.d.ts.map +1 -0
- package/dist/decide.js +38 -0
- package/dist/decide.js.map +1 -0
- package/dist/diagnostics.d.ts +11 -0
- package/dist/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics.js +51 -0
- package/dist/diagnostics.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +27 -0
- package/dist/index.js.map +1 -0
- package/dist/internal/base64url.d.ts +8 -0
- package/dist/internal/base64url.d.ts.map +1 -0
- package/dist/internal/base64url.js +32 -0
- package/dist/internal/base64url.js.map +1 -0
- package/dist/internal/iso-date.d.ts +8 -0
- package/dist/internal/iso-date.d.ts.map +1 -0
- package/dist/internal/iso-date.js +39 -0
- package/dist/internal/iso-date.js.map +1 -0
- package/dist/internal/origin.d.ts +6 -0
- package/dist/internal/origin.d.ts.map +1 -0
- package/dist/internal/origin.js +19 -0
- package/dist/internal/origin.js.map +1 -0
- package/dist/internal/plain-object.d.ts +8 -0
- package/dist/internal/plain-object.d.ts.map +1 -0
- package/dist/internal/plain-object.js +16 -0
- package/dist/internal/plain-object.js.map +1 -0
- package/dist/internal/theme-key.d.ts +2 -0
- package/dist/internal/theme-key.d.ts.map +1 -0
- package/dist/internal/theme-key.js +13 -0
- package/dist/internal/theme-key.js.map +1 -0
- package/dist/manifest.d.ts +13 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +153 -0
- package/dist/manifest.js.map +1 -0
- package/dist/page/main.d.ts +2 -0
- package/dist/page/main.d.ts.map +1 -0
- package/dist/page/main.js +88 -0
- package/dist/page/main.js.map +1 -0
- package/dist/page/messages.d.ts +24 -0
- package/dist/page/messages.d.ts.map +1 -0
- package/dist/page/messages.js +43 -0
- package/dist/page/messages.js.map +1 -0
- package/dist/page/model.d.ts +16 -0
- package/dist/page/model.d.ts.map +1 -0
- package/dist/page/model.js +51 -0
- package/dist/page/model.js.map +1 -0
- package/dist/page/render.d.ts +24 -0
- package/dist/page/render.d.ts.map +1 -0
- package/dist/page/render.js +61 -0
- package/dist/page/render.js.map +1 -0
- package/dist/resolve.d.ts +31 -0
- package/dist/resolve.d.ts.map +1 -0
- package/dist/resolve.js +65 -0
- package/dist/resolve.js.map +1 -0
- package/dist/return-path.d.ts +10 -0
- package/dist/return-path.d.ts.map +1 -0
- package/dist/return-path.js +75 -0
- package/dist/return-path.js.map +1 -0
- package/dist/select.d.ts +16 -0
- package/dist/select.d.ts.map +1 -0
- package/dist/select.js +17 -0
- package/dist/select.js.map +1 -0
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +10 -0
- package/dist/types.js.map +1 -0
- package/dist/update.d.ts +28 -0
- package/dist/update.d.ts.map +1 -0
- package/dist/update.js +52 -0
- package/dist/update.js.map +1 -0
- package/dist/vite/default-style.d.ts +2 -0
- package/dist/vite/default-style.d.ts.map +1 -0
- package/dist/vite/default-style.js +109 -0
- package/dist/vite/default-style.js.map +1 -0
- package/dist/vite/entry-page.d.ts +6 -0
- package/dist/vite/entry-page.d.ts.map +1 -0
- package/dist/vite/entry-page.js +22 -0
- package/dist/vite/entry-page.js.map +1 -0
- package/dist/vite/index.d.ts +24 -0
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +190 -0
- package/dist/vite/index.js.map +1 -0
- package/dist/vite/options.d.ts +31 -0
- package/dist/vite/options.d.ts.map +1 -0
- package/dist/vite/options.js +118 -0
- package/dist/vite/options.js.map +1 -0
- package/package.json +53 -0
package/dist/update.js
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// Write path behind spec/pwa-entry-resilience.md's revised page-side API, `updateEntryManifest(data)`: validates
|
|
2
|
+
// the plain object the application hands in (src/manifest.ts), decides whether it beats what is already stored
|
|
3
|
+
// (src/select.ts), and writes it if so. Not part of the package's public API on its own — src/client/index.ts
|
|
4
|
+
// wires the exported `updateEntryManifest` to this function, supplying the real browser ports (IndexedDB,
|
|
5
|
+
// `Date.now`) via src/browser/index.ts's `createEntryRuntimePorts`.
|
|
6
|
+
import { diagnostic } from "./diagnostics.js";
|
|
7
|
+
import { parseEntryManifest } from "./manifest.js";
|
|
8
|
+
import { selectManifest } from "./select.js";
|
|
9
|
+
/**
|
|
10
|
+
* Validates `data`, compares it against the stored record, and writes it when it wins. Never throws: every port
|
|
11
|
+
* failure (an unavailable clock, a failed read, a failed write) becomes `{ accepted: false, diagnostics }` rather
|
|
12
|
+
* than a rejection — there is no room for a diagnostic on the `accepted: true` branch, so any failure that cannot
|
|
13
|
+
* be represented there must reject instead of silently pretending to have succeeded.
|
|
14
|
+
*/
|
|
15
|
+
export async function runEntryManifestUpdate(data, config, ports) {
|
|
16
|
+
let now;
|
|
17
|
+
try {
|
|
18
|
+
now = ports.now();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return { accepted: false, diagnostics: [diagnostic("entry.runtime-unavailable", "")] };
|
|
22
|
+
}
|
|
23
|
+
if (!Number.isFinite(now)) {
|
|
24
|
+
return { accepted: false, diagnostics: [diagnostic("entry.runtime-unavailable", "")] };
|
|
25
|
+
}
|
|
26
|
+
const parsed = parseEntryManifest(data, {
|
|
27
|
+
appId: config.appId,
|
|
28
|
+
environment: config.environment,
|
|
29
|
+
maxValidityDays: config.maxValidityDays,
|
|
30
|
+
now,
|
|
31
|
+
});
|
|
32
|
+
if (!parsed.ok)
|
|
33
|
+
return { accepted: false, diagnostics: parsed.diagnostics };
|
|
34
|
+
let stored;
|
|
35
|
+
try {
|
|
36
|
+
stored = await ports.loadStored();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return { accepted: false, diagnostics: [diagnostic("entry.storage-unavailable", "")] };
|
|
40
|
+
}
|
|
41
|
+
const selection = selectManifest({ stored, incoming: parsed.manifest });
|
|
42
|
+
if (selection.kind === "rejected")
|
|
43
|
+
return { accepted: false, diagnostics: selection.diagnostics };
|
|
44
|
+
try {
|
|
45
|
+
await ports.saveStored(selection.manifest);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
48
|
+
return { accepted: false, diagnostics: [diagnostic("entry.storage-write-failed", "")] };
|
|
49
|
+
}
|
|
50
|
+
return { accepted: true, sequence: selection.manifest.sequence };
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=update.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.js","sourceRoot":"","sources":["../src/update.ts"],"names":[],"mappings":"AAAA,iHAAiH;AACjH,+GAA+G;AAC/G,8GAA8G;AAC9G,0GAA0G;AAC1G,oEAAoE;AACpE,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAoB7C;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,IAAa,EACb,MAAiC,EACjC,KAA+B;IAE/B,IAAI,GAAW,CAAC;IAChB,IAAI,CAAC;QACH,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC;IAED,MAAM,MAAM,GAAG,kBAAkB,CAAC,IAAI,EAAE;QACtC,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,eAAe,EAAE,MAAM,CAAC,eAAe;QACvC,GAAG;KACJ,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,EAAE;QAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;IAE5E,IAAI,MAA4B,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC;IACpC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;IACzF,CAAC;IAED,MAAM,SAAS,GAAG,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxE,IAAI,SAAS,CAAC,IAAI,KAAK,UAAU;QAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC;IAElG,IAAI,CAAC;QACH,MAAM,KAAK,CAAC,UAAU,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,CAAC,UAAU,CAAC,4BAA4B,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC;IAC1F,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export declare const DEFAULT_RECOVERY_PAGE_STYLE = ".pwa-entry {\n --pwa-entry-bg: #ffffff;\n --pwa-entry-fg: #1f2328;\n --pwa-entry-muted: #5b6470;\n --pwa-entry-accent: #0b5cd5;\n --pwa-entry-accent-fg: #ffffff;\n --pwa-entry-radius: 0.5rem;\n --pwa-entry-max-width: 34rem;\n --pwa-entry-font: -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, Helvetica, Arial, sans-serif;\n /* Covers the whole viewport, whatever the body's default margin: until 2026-09-24 the root was a centred\n max-width column, so in dark mode only that column was dark and the rest of the page stayed white. The content\n keeps its width through the inline padding instead of a max-width on the root. */\n display: block;\n position: fixed;\n inset: 0;\n overflow-y: auto;\n box-sizing: border-box;\n color-scheme: light;\n padding: 3rem max(1.5rem, calc((100% - var(--pwa-entry-max-width)) / 2));\n font-family: var(--pwa-entry-font);\n line-height: 1.5;\n color: var(--pwa-entry-fg);\n background: var(--pwa-entry-bg);\n}\n@media (prefers-color-scheme: dark) {\n .pwa-entry:not([data-theme=\"light\"]) {\n --pwa-entry-bg: #0f1419;\n --pwa-entry-fg: #e6e9ec;\n --pwa-entry-muted: #9aa4b0;\n --pwa-entry-accent: #4c93ff;\n --pwa-entry-accent-fg: #0b1220;\n color-scheme: dark;\n }\n}\n.pwa-entry[data-theme=\"dark\"] {\n --pwa-entry-bg: #0f1419;\n --pwa-entry-fg: #e6e9ec;\n --pwa-entry-muted: #9aa4b0;\n --pwa-entry-accent: #4c93ff;\n --pwa-entry-accent-fg: #0b1220;\n color-scheme: dark;\n}\n.pwa-entry__headline {\n margin: 0 0 0.75rem;\n font-size: 1.375rem;\n font-weight: 600;\n}\n.pwa-entry__message,\n.pwa-entry__expiry {\n margin: 0 0 0.75rem;\n color: var(--pwa-entry-muted);\n}\n.pwa-entry__list {\n display: grid;\n gap: 0.75rem;\n margin: 1.5rem 0 0;\n padding: 0;\n list-style: none;\n}\n.pwa-entry__item {\n margin: 0;\n padding: 0;\n}\n.pwa-entry__button {\n display: block;\n box-sizing: border-box;\n width: 100%;\n min-height: 44px;\n padding: 0.75rem 1rem;\n border: none;\n border-radius: var(--pwa-entry-radius);\n background: var(--pwa-entry-accent);\n color: var(--pwa-entry-accent-fg);\n font: inherit;\n font-weight: 600;\n cursor: pointer;\n}\n.pwa-entry__button:active {\n /* A brightness shift rather than a different background: swapping in a surface colour while the label keeps\n --pwa-entry-accent-fg leaves white text on a near-white fill for the light theme. */\n filter: brightness(0.92);\n}\n.pwa-entry__empty,\n.pwa-entry__loading {\n color: var(--pwa-entry-muted);\n}\n@media print {\n .pwa-entry {\n position: static;\n overflow: visible;\n }\n}\n";
|
|
2
|
+
//# sourceMappingURL=default-style.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-style.d.ts","sourceRoot":"","sources":["../../src/vite/default-style.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,2BAA2B,yjFA4FvC,CAAC"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// The recovery page's default stylesheet — spec/pwa-entry-resilience.md's "修订:恢复页的默认样式与宿主定制". A
|
|
2
|
+
// single constant, not inlined into src/vite/index.ts's HTML string, so it can be reviewed on its own and hashed
|
|
3
|
+
// for CSP there (see that module's generateBundle). Fixed text only: nothing here comes from plugin configuration
|
|
4
|
+
// — a host's own CSS is appended separately, verbatim, by the `css` option (src/vite/options.ts).
|
|
5
|
+
//
|
|
6
|
+
// Colour tokens are CSS custom properties so a host can re-skin the page by resetting them alone (spec's "宿主换肤
|
|
7
|
+
// 的写法"): light values live directly on `.pwa-entry` (also carrying the page's only layout rules — system font
|
|
8
|
+
// stack, a constrained max width, block-level buttons with a comfortable tap target; no icons, web fonts, images or
|
|
9
|
+
// animations), dark values are redefined under `prefers-color-scheme: dark` unless `data-theme="light"` overrides
|
|
10
|
+
// it, and `[data-theme="dark"]` overrides the system preference outright — the exact three-selector precedence
|
|
11
|
+
// contract the spec pins, and the values in both tables, character for character. `data-theme` itself is set by
|
|
12
|
+
// src/page/main.ts before the first render, from the stored preference `setPwaTheme` (src/client/index.ts) writes.
|
|
13
|
+
//
|
|
14
|
+
// Every class selector below must be exactly the set src/page/render.ts's renderPage produces — see this module's
|
|
15
|
+
// own class-table comment and test/page/class-alignment.test.ts, which checks both directions mechanically.
|
|
16
|
+
export const DEFAULT_RECOVERY_PAGE_STYLE = `.pwa-entry {
|
|
17
|
+
--pwa-entry-bg: #ffffff;
|
|
18
|
+
--pwa-entry-fg: #1f2328;
|
|
19
|
+
--pwa-entry-muted: #5b6470;
|
|
20
|
+
--pwa-entry-accent: #0b5cd5;
|
|
21
|
+
--pwa-entry-accent-fg: #ffffff;
|
|
22
|
+
--pwa-entry-radius: 0.5rem;
|
|
23
|
+
--pwa-entry-max-width: 34rem;
|
|
24
|
+
--pwa-entry-font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
25
|
+
/* Covers the whole viewport, whatever the body's default margin: until 2026-09-24 the root was a centred
|
|
26
|
+
max-width column, so in dark mode only that column was dark and the rest of the page stayed white. The content
|
|
27
|
+
keeps its width through the inline padding instead of a max-width on the root. */
|
|
28
|
+
display: block;
|
|
29
|
+
position: fixed;
|
|
30
|
+
inset: 0;
|
|
31
|
+
overflow-y: auto;
|
|
32
|
+
box-sizing: border-box;
|
|
33
|
+
color-scheme: light;
|
|
34
|
+
padding: 3rem max(1.5rem, calc((100% - var(--pwa-entry-max-width)) / 2));
|
|
35
|
+
font-family: var(--pwa-entry-font);
|
|
36
|
+
line-height: 1.5;
|
|
37
|
+
color: var(--pwa-entry-fg);
|
|
38
|
+
background: var(--pwa-entry-bg);
|
|
39
|
+
}
|
|
40
|
+
@media (prefers-color-scheme: dark) {
|
|
41
|
+
.pwa-entry:not([data-theme="light"]) {
|
|
42
|
+
--pwa-entry-bg: #0f1419;
|
|
43
|
+
--pwa-entry-fg: #e6e9ec;
|
|
44
|
+
--pwa-entry-muted: #9aa4b0;
|
|
45
|
+
--pwa-entry-accent: #4c93ff;
|
|
46
|
+
--pwa-entry-accent-fg: #0b1220;
|
|
47
|
+
color-scheme: dark;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
.pwa-entry[data-theme="dark"] {
|
|
51
|
+
--pwa-entry-bg: #0f1419;
|
|
52
|
+
--pwa-entry-fg: #e6e9ec;
|
|
53
|
+
--pwa-entry-muted: #9aa4b0;
|
|
54
|
+
--pwa-entry-accent: #4c93ff;
|
|
55
|
+
--pwa-entry-accent-fg: #0b1220;
|
|
56
|
+
color-scheme: dark;
|
|
57
|
+
}
|
|
58
|
+
.pwa-entry__headline {
|
|
59
|
+
margin: 0 0 0.75rem;
|
|
60
|
+
font-size: 1.375rem;
|
|
61
|
+
font-weight: 600;
|
|
62
|
+
}
|
|
63
|
+
.pwa-entry__message,
|
|
64
|
+
.pwa-entry__expiry {
|
|
65
|
+
margin: 0 0 0.75rem;
|
|
66
|
+
color: var(--pwa-entry-muted);
|
|
67
|
+
}
|
|
68
|
+
.pwa-entry__list {
|
|
69
|
+
display: grid;
|
|
70
|
+
gap: 0.75rem;
|
|
71
|
+
margin: 1.5rem 0 0;
|
|
72
|
+
padding: 0;
|
|
73
|
+
list-style: none;
|
|
74
|
+
}
|
|
75
|
+
.pwa-entry__item {
|
|
76
|
+
margin: 0;
|
|
77
|
+
padding: 0;
|
|
78
|
+
}
|
|
79
|
+
.pwa-entry__button {
|
|
80
|
+
display: block;
|
|
81
|
+
box-sizing: border-box;
|
|
82
|
+
width: 100%;
|
|
83
|
+
min-height: 44px;
|
|
84
|
+
padding: 0.75rem 1rem;
|
|
85
|
+
border: none;
|
|
86
|
+
border-radius: var(--pwa-entry-radius);
|
|
87
|
+
background: var(--pwa-entry-accent);
|
|
88
|
+
color: var(--pwa-entry-accent-fg);
|
|
89
|
+
font: inherit;
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
cursor: pointer;
|
|
92
|
+
}
|
|
93
|
+
.pwa-entry__button:active {
|
|
94
|
+
/* A brightness shift rather than a different background: swapping in a surface colour while the label keeps
|
|
95
|
+
--pwa-entry-accent-fg leaves white text on a near-white fill for the light theme. */
|
|
96
|
+
filter: brightness(0.92);
|
|
97
|
+
}
|
|
98
|
+
.pwa-entry__empty,
|
|
99
|
+
.pwa-entry__loading {
|
|
100
|
+
color: var(--pwa-entry-muted);
|
|
101
|
+
}
|
|
102
|
+
@media print {
|
|
103
|
+
.pwa-entry {
|
|
104
|
+
position: static;
|
|
105
|
+
overflow: visible;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
`;
|
|
109
|
+
//# sourceMappingURL=default-style.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-style.js","sourceRoot":"","sources":["../../src/vite/default-style.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,iHAAiH;AACjH,kHAAkH;AAClH,kGAAkG;AAClG,EAAE;AACF,+GAA+G;AAC/G,8GAA8G;AAC9G,oHAAoH;AACpH,kHAAkH;AAClH,+GAA+G;AAC/G,gHAAgH;AAChH,mHAAmH;AACnH,EAAE;AACF,kHAAkH;AAClH,4GAA4G;AAC5G,MAAM,CAAC,MAAM,2BAA2B,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4F1C,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `moduleUrl` is the caller's own `import.meta.url`, passed in rather than read here so this stays a plain
|
|
3
|
+
* function a test can call with either extension without needing two real files on disk.
|
|
4
|
+
*/
|
|
5
|
+
export declare function resolveEntryPageId(moduleUrl: string): string;
|
|
6
|
+
//# sourceMappingURL=entry-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-page.d.ts","sourceRoot":"","sources":["../../src/vite/entry-page.ts"],"names":[],"mappings":"AAcA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAG5D"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// Resolves the recovery page script's build entry (`src/page/main.ts`) to an absolute path the plugin can hand to
|
|
2
|
+
// `this.emitFile({ type: "chunk", ... })`.
|
|
3
|
+
//
|
|
4
|
+
// This module's own `import.meta.url` tells the whole story: under Vitest the plugin runs straight from
|
|
5
|
+
// `src/vite/index.ts`, so its sibling `../page/main.ts` exists on disk and TypeScript's own extension is `.ts`;
|
|
6
|
+
// inside a real build the plugin runs from the compiled `dist/vite/index.js`, whose sibling is the compiled
|
|
7
|
+
// `../page/main.js`, and `dist/page/main.ts` does not exist at all — `tsc` only ever emits `.js`/`.d.ts` there.
|
|
8
|
+
// Both cases turn on the same fact: this module's own extension. `src/` may not import `node:fs` (see
|
|
9
|
+
// ADR-0018, "src/ 环境中立"), so the sibling's existence cannot be checked directly, but it never needs to be —
|
|
10
|
+
// this module's own URL already carries the same information a filesystem check would.
|
|
11
|
+
const TS_EXTENSION = ".ts";
|
|
12
|
+
const ENTRY_PAGE_TS = "../page/main.ts";
|
|
13
|
+
const ENTRY_PAGE_JS = "../page/main.js";
|
|
14
|
+
/**
|
|
15
|
+
* `moduleUrl` is the caller's own `import.meta.url`, passed in rather than read here so this stays a plain
|
|
16
|
+
* function a test can call with either extension without needing two real files on disk.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveEntryPageId(moduleUrl) {
|
|
19
|
+
const sibling = moduleUrl.endsWith(TS_EXTENSION) ? ENTRY_PAGE_TS : ENTRY_PAGE_JS;
|
|
20
|
+
return decodeURIComponent(new URL(sibling, moduleUrl).pathname);
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=entry-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entry-page.js","sourceRoot":"","sources":["../../src/vite/entry-page.ts"],"names":[],"mappings":"AAAA,kHAAkH;AAClH,2CAA2C;AAC3C,EAAE;AACF,wGAAwG;AACxG,gHAAgH;AAChH,4GAA4G;AAC5G,gHAAgH;AAChH,sGAAsG;AACtG,4GAA4G;AAC5G,uFAAuF;AACvF,MAAM,YAAY,GAAG,KAAK,CAAC;AAC3B,MAAM,aAAa,GAAG,iBAAiB,CAAC;AACxC,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAExC;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,SAAiB;IAClD,MAAM,OAAO,GAAG,SAAS,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC;IACjF,OAAO,kBAAkB,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import type { EntryCheckerConfig } from "../browser/index.js";
|
|
3
|
+
import type { PwaEntryPageLocale, PwaEntryPageMessages } from "../page/messages.js";
|
|
4
|
+
import type { PwaEntryResilienceOptions } from "./options.js";
|
|
5
|
+
export type { PwaEntryResilienceOptions } from "./options.js";
|
|
6
|
+
export type { PwaEntryPageLocale, PwaEntryPageMessages } from "../page/messages.js";
|
|
7
|
+
/** The virtual module's full shape: `EntryCheckerConfig` (what `createEntryRuntimePorts` needs) plus the resolved
|
|
8
|
+
* `locale` and fully-merged `messages` the recovery page (`src/page/main.ts`) renders with. */
|
|
9
|
+
export type EntryPageVirtualConfig = EntryCheckerConfig & {
|
|
10
|
+
readonly locale: PwaEntryPageLocale;
|
|
11
|
+
readonly messages: PwaEntryPageMessages;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Creates the entry-resilience Vite plugin.
|
|
15
|
+
*
|
|
16
|
+
* Options are validated here (see src/vite/options.ts: a bad `maxValidityDays` should fail while the developer is
|
|
17
|
+
* still looking at vite.config).
|
|
18
|
+
*
|
|
19
|
+
* `configResolved` finds `pwa()` by its published `PWA_PLUGIN_NAME` rather than by requiring the app to pass a
|
|
20
|
+
* reference to it: `pwa()` returns `Plugin<PwaPluginApi>`, but nothing in this plugin's own options threads that
|
|
21
|
+
* object through, and looking it up by name is exactly what `PWA_PLUGIN_NAME` publishes it for.
|
|
22
|
+
*/
|
|
23
|
+
export declare function pwaEntryResilience(options: PwaEntryResilienceOptions): Plugin;
|
|
24
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAI9D,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIpF,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAE9D,YAAY,EAAE,yBAAyB,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEpF;gGACgG;AAChG,MAAM,MAAM,sBAAsB,GAAG,kBAAkB,GAAG;IACxD,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,oBAAoB,CAAC;CACzC,CAAC;AA8DF;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAuH7E"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// The Vite plugin that publishes the entry-recovery page's build-time half: the recovery page, its fingerprinted
|
|
2
|
+
// script, and the virtual config module every page-side entry reads. Runs alongside `pwa()` (`@pwa-platform/vite`),
|
|
3
|
+
// never in place of it — see spec/pwa-entry-resilience.md's "构建集成" and ADR-0018's "构建集成是与 pwa() 并列的独
|
|
4
|
+
// 立 Vite 插件".
|
|
5
|
+
//
|
|
6
|
+
// ADR-0033 (2026-09-23) removed the build-time seed and its signature verification: the platform no longer holds a
|
|
7
|
+
// trust root, so there is nothing left to verify in `buildStart` before emitting the recovery page's chunk.
|
|
8
|
+
import { PWA_PLUGIN_NAME } from "@pwa-platform/vite";
|
|
9
|
+
import { diagnostic } from "../diagnostics.js";
|
|
10
|
+
import { mergeEntryPageMessages } from "../page/messages.js";
|
|
11
|
+
import { DEFAULT_RECOVERY_PAGE_STYLE } from "./default-style.js";
|
|
12
|
+
import { resolveEntryPageId } from "./entry-page.js";
|
|
13
|
+
import { failWithDiagnostic, validatePwaEntryResilienceOptions } from "./options.js";
|
|
14
|
+
const PLUGIN_NAME = "pwa-platform-entry-resilience";
|
|
15
|
+
const ENTRY_CONFIG_MODULE_ID = "virtual:pwa-entry-config";
|
|
16
|
+
const ENTRY_CONFIG_RESOLVED_ID = `\0${ENTRY_CONFIG_MODULE_ID}`;
|
|
17
|
+
const RECOVERY_PAGE_ASSET_NAME = "pwa-entry.html";
|
|
18
|
+
const RECOVERY_PAGE_CHUNK_NAME = "pwa-entry";
|
|
19
|
+
function formatDiagnostics(diagnostics) {
|
|
20
|
+
return diagnostics.map(({ code, path }) => `${code} at ${path === "" ? "(root)" : path}`).join(", ");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* `sha256-<base64>`, the exact form CSP's `style-src` expects, for one inline style block's text — see
|
|
24
|
+
* spec/pwa-entry-resilience.md's "CSP 支持". `crypto` and `btoa` are Web APIs Node exposes globally (no import),
|
|
25
|
+
* the same reason ADR-0018's "src/ 环境中立" let this package's now-removed signature verification use
|
|
26
|
+
* `crypto.subtle` directly; `btoa` only encodes these locally-computed, trusted digest bytes for a build log, never
|
|
27
|
+
* decodes untrusted input (contrast src/internal/base64url.ts, which avoids `atob` for exactly that reason).
|
|
28
|
+
*/
|
|
29
|
+
async function inlineStyleHash(text) {
|
|
30
|
+
const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
31
|
+
let binary = "";
|
|
32
|
+
for (const byte of new Uint8Array(digest))
|
|
33
|
+
binary += String.fromCharCode(byte);
|
|
34
|
+
return `sha256-${btoa(binary)}`;
|
|
35
|
+
}
|
|
36
|
+
const HTML_ESCAPES = {
|
|
37
|
+
"&": "&",
|
|
38
|
+
"<": "<",
|
|
39
|
+
">": ">",
|
|
40
|
+
'"': """,
|
|
41
|
+
"'": "'",
|
|
42
|
+
};
|
|
43
|
+
/** Escapes the five HTML-significant characters — used only for `<title>`, the one place plugin-configured text
|
|
44
|
+
* (`documentTitle`) is written into the shell itself rather than through the page's own `textContent`-only
|
|
45
|
+
* rendering (src/page/render.ts). Mirrors packages/vite/src/offline-page.ts's `escapeHtml`; not shared, per this
|
|
46
|
+
* revision's "同一套选项形态与校验方式,但不共享代码". */
|
|
47
|
+
function escapeHtml(value) {
|
|
48
|
+
return value.replace(/[&<>"']/g, (char) => HTML_ESCAPES[char] ?? char);
|
|
49
|
+
}
|
|
50
|
+
/** Every chunk the recovery page script statically imports, transitively, including the entry chunk itself. */
|
|
51
|
+
function collectStaticImportClosure(bundle, entryFileName) {
|
|
52
|
+
const visited = new Set();
|
|
53
|
+
const stack = [entryFileName];
|
|
54
|
+
for (;;) {
|
|
55
|
+
const fileName = stack.pop();
|
|
56
|
+
if (fileName === undefined)
|
|
57
|
+
break;
|
|
58
|
+
if (visited.has(fileName))
|
|
59
|
+
continue;
|
|
60
|
+
visited.add(fileName);
|
|
61
|
+
const chunk = bundle[fileName];
|
|
62
|
+
if (chunk !== undefined && chunk.type === "chunk")
|
|
63
|
+
stack.push(...chunk.imports);
|
|
64
|
+
}
|
|
65
|
+
return visited;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates the entry-resilience Vite plugin.
|
|
69
|
+
*
|
|
70
|
+
* Options are validated here (see src/vite/options.ts: a bad `maxValidityDays` should fail while the developer is
|
|
71
|
+
* still looking at vite.config).
|
|
72
|
+
*
|
|
73
|
+
* `configResolved` finds `pwa()` by its published `PWA_PLUGIN_NAME` rather than by requiring the app to pass a
|
|
74
|
+
* reference to it: `pwa()` returns `Plugin<PwaPluginApi>`, but nothing in this plugin's own options threads that
|
|
75
|
+
* object through, and looking it up by name is exactly what `PWA_PLUGIN_NAME` publishes it for.
|
|
76
|
+
*/
|
|
77
|
+
export function pwaEntryResilience(options) {
|
|
78
|
+
const validated = validatePwaEntryResilienceOptions(options);
|
|
79
|
+
const { identity, maxValidityDays, css, locale, messages } = validated;
|
|
80
|
+
const resolvedMessages = mergeEntryPageMessages(locale, messages);
|
|
81
|
+
let base = "";
|
|
82
|
+
let platformApi;
|
|
83
|
+
// Set once `buildStart` has emitted the recovery page's script chunk; `generateBundle` resolves its published
|
|
84
|
+
// file name from the reference id, and `writeBundle` reuses that resolved name rather than re-resolving it.
|
|
85
|
+
let entryPageChunkRefId;
|
|
86
|
+
let entryPageFileName;
|
|
87
|
+
return {
|
|
88
|
+
name: PLUGIN_NAME,
|
|
89
|
+
apply: "build",
|
|
90
|
+
configResolved(config) {
|
|
91
|
+
// `base` is read once, here, for the same reason `@pwa-platform/vite`'s own plugin reads it in this hook
|
|
92
|
+
// rather than later: a later plugin mutating the resolved config must not change what this plugin checks.
|
|
93
|
+
base = config.base;
|
|
94
|
+
if (base !== identity.mountPath)
|
|
95
|
+
failWithDiagnostic("entry.base-mismatch", "/base");
|
|
96
|
+
const platformPlugin = config.plugins.find((plugin) => plugin.name === PWA_PLUGIN_NAME);
|
|
97
|
+
if (platformPlugin === undefined)
|
|
98
|
+
failWithDiagnostic("entry.platform-plugin-missing", "");
|
|
99
|
+
platformApi = platformPlugin.api;
|
|
100
|
+
},
|
|
101
|
+
buildStart() {
|
|
102
|
+
entryPageChunkRefId = this.emitFile({
|
|
103
|
+
type: "chunk",
|
|
104
|
+
id: resolveEntryPageId(import.meta.url),
|
|
105
|
+
name: RECOVERY_PAGE_CHUNK_NAME,
|
|
106
|
+
});
|
|
107
|
+
},
|
|
108
|
+
resolveId(id) {
|
|
109
|
+
return id === ENTRY_CONFIG_MODULE_ID ? ENTRY_CONFIG_RESOLVED_ID : null;
|
|
110
|
+
},
|
|
111
|
+
load(id) {
|
|
112
|
+
if (id !== ENTRY_CONFIG_RESOLVED_ID)
|
|
113
|
+
return null;
|
|
114
|
+
// Built from the validated options alone: like `@pwa-platform/vite`'s own client-config virtual module, this
|
|
115
|
+
// runs while the module graph is assembled, long before `generateBundle` or `writeBundle` produce anything
|
|
116
|
+
// this config could otherwise have read.
|
|
117
|
+
const config = {
|
|
118
|
+
appId: identity.appId,
|
|
119
|
+
environment: identity.environment,
|
|
120
|
+
scope: identity.scope,
|
|
121
|
+
mountPath: identity.mountPath,
|
|
122
|
+
maxValidityDays,
|
|
123
|
+
recoveryPagePath: `${identity.mountPath}${RECOVERY_PAGE_ASSET_NAME}`,
|
|
124
|
+
locale,
|
|
125
|
+
messages: resolvedMessages,
|
|
126
|
+
};
|
|
127
|
+
return `export default Object.freeze(${JSON.stringify(config)});\n`;
|
|
128
|
+
},
|
|
129
|
+
async generateBundle() {
|
|
130
|
+
// `buildStart` failing calls `this.error`, which always throws, so the build never reaches this hook without
|
|
131
|
+
// a ref id — this guard only documents that invariant, it does not paper over a real failure.
|
|
132
|
+
if (entryPageChunkRefId === undefined)
|
|
133
|
+
return;
|
|
134
|
+
entryPageFileName = this.getFileName(entryPageChunkRefId);
|
|
135
|
+
// Default style first, host `css` (already validated in src/vite/options.ts) appended verbatim right after
|
|
136
|
+
// it — spec's "构建时作为第二段内联 <style>,紧随默认样式之后写入同一文档". Each block's CSP hash is logged
|
|
137
|
+
// through Vite's `this.info`, never written to a file (spec's "不生成额外文件").
|
|
138
|
+
//
|
|
139
|
+
// A CSP hash covers the element's whole text content, and the parser keeps the newline right after `<style>`
|
|
140
|
+
// (it drops it only for <pre>, <listing> and <textarea>). Each hash is therefore taken over exactly the string
|
|
141
|
+
// written between the tags. Until 2026-09-24 the hash left that newline out, so a site copying the logged
|
|
142
|
+
// values into a hash-only `style-src` had the stylesheet blocked (measured in Chrome 153); the page bytes are
|
|
143
|
+
// unchanged by the fix, only the logged values are.
|
|
144
|
+
const defaultStyleContent = `\n${DEFAULT_RECOVERY_PAGE_STYLE}`;
|
|
145
|
+
let styleBlocks = `<style>${defaultStyleContent}</style>`;
|
|
146
|
+
this.info(`pwa-entry.html style (default): ${await inlineStyleHash(defaultStyleContent)}`);
|
|
147
|
+
if (css !== undefined) {
|
|
148
|
+
const hostStyleContent = `\n${css}`;
|
|
149
|
+
styleBlocks += `\n<style>${hostStyleContent}</style>`;
|
|
150
|
+
this.info(`pwa-entry.html style (host css): ${await inlineStyleHash(hostStyleContent)}`);
|
|
151
|
+
}
|
|
152
|
+
// Fixed shell otherwise, aside from `lang` and `<title>`: everything else reaches the page solely through the
|
|
153
|
+
// virtual module `src/page/main.ts` imports, never written into this shell. The charset is declared in the
|
|
154
|
+
// document because the page can render non-ASCII text and the platform cannot rely on every deployment
|
|
155
|
+
// sending a charset in Content-Type.
|
|
156
|
+
const html = "<!doctype html>\n" +
|
|
157
|
+
`<html lang="${locale}">\n` +
|
|
158
|
+
"<head>\n" +
|
|
159
|
+
'<meta charset="utf-8">\n' +
|
|
160
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">\n' +
|
|
161
|
+
`<title>${escapeHtml(resolvedMessages.documentTitle)}</title>\n` +
|
|
162
|
+
`${styleBlocks}\n` +
|
|
163
|
+
"</head>\n" +
|
|
164
|
+
"<body>\n" +
|
|
165
|
+
'<main id="pwa-entry"></main>\n' +
|
|
166
|
+
`<script type="module" src="${base}${entryPageFileName}"></script>\n` +
|
|
167
|
+
"</body>\n" +
|
|
168
|
+
"</html>\n";
|
|
169
|
+
this.emitFile({ type: "asset", fileName: RECOVERY_PAGE_ASSET_NAME, source: html });
|
|
170
|
+
},
|
|
171
|
+
writeBundle(_outputOptions, bundle) {
|
|
172
|
+
if (entryPageFileName === undefined)
|
|
173
|
+
return;
|
|
174
|
+
// Read here, not in `generateBundle`: `@pwa-platform/vite`'s own plugin only exposes its compiled plan once
|
|
175
|
+
// its `generateBundle` has run, and hook order between sibling plugins within one phase is not guaranteed.
|
|
176
|
+
const plan = platformApi?.getPlan() ?? null;
|
|
177
|
+
if (plan === null)
|
|
178
|
+
throw new Error(formatDiagnostics([diagnostic("entry.platform-plan-unavailable", "")]));
|
|
179
|
+
const closure = collectStaticImportClosure(bundle, entryPageFileName);
|
|
180
|
+
const requiredUrls = [
|
|
181
|
+
`${base}${RECOVERY_PAGE_ASSET_NAME}`,
|
|
182
|
+
...[...closure].map((fileName) => `${base}${fileName}`),
|
|
183
|
+
];
|
|
184
|
+
const missing = requiredUrls.some((url) => !plan.precache.some((entry) => entry.url === url));
|
|
185
|
+
if (missing)
|
|
186
|
+
throw new Error(formatDiagnostics([diagnostic("entry.recovery-page-not-precached", "/precache")]));
|
|
187
|
+
},
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/vite/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PwaIdentity } from "@pwa-platform/contracts";
|
|
2
|
+
import type { EntryDiagnosticCode, EntryDiagnosticPath } from "../diagnostics.js";
|
|
3
|
+
import type { PwaEntryPageLocale, PwaEntryPageMessages } from "../page/messages.js";
|
|
4
|
+
export type PwaEntryResilienceOptions = {
|
|
5
|
+
readonly identity: PwaIdentity;
|
|
6
|
+
/** Integer 1-90; defaults to 30. */
|
|
7
|
+
readonly maxValidityDays?: number;
|
|
8
|
+
/** Host CSS appended verbatim, as a second inline `<style>`, right after the platform's default stylesheet —
|
|
9
|
+
* see spec/pwa-entry-resilience.md's "修订:恢复页的默认样式与宿主定制". The platform does not parse or clean
|
|
10
|
+
* it: the host is responsible for it being valid CSS. The only rule enforced here is that it must not contain
|
|
11
|
+
* the literal sequence `</style`, which would close the style block early. */
|
|
12
|
+
readonly css?: string;
|
|
13
|
+
/** Fixed at build time; no runtime switching. Defaults to `"zh-CN"` — see spec/pwa-entry-resilience.md's "修
|
|
14
|
+
* 订:恢复页的构建期语言与文案覆盖". */
|
|
15
|
+
readonly locale?: PwaEntryPageLocale;
|
|
16
|
+
/** Overrides some or all of the chosen locale's built-in copy (src/page/messages.ts). */
|
|
17
|
+
readonly messages?: Partial<PwaEntryPageMessages>;
|
|
18
|
+
};
|
|
19
|
+
export type PwaEntryResilienceValidatedOptions = {
|
|
20
|
+
readonly identity: PwaIdentity;
|
|
21
|
+
readonly maxValidityDays: number;
|
|
22
|
+
readonly css: string | undefined;
|
|
23
|
+
readonly locale: PwaEntryPageLocale;
|
|
24
|
+
readonly messages: Partial<PwaEntryPageMessages> | undefined;
|
|
25
|
+
};
|
|
26
|
+
/** Throws a `TypeError` naming a diagnostic code and path; never returns. `detail`, when given, is appended for a
|
|
27
|
+
* code whose fix isn't obvious from the code and path alone. */
|
|
28
|
+
export declare function failWithDiagnostic(code: EntryDiagnosticCode, path: EntryDiagnosticPath, detail?: string): never;
|
|
29
|
+
/** Validates and normalizes the plugin's options, throwing for anything a build would reject later. */
|
|
30
|
+
export declare function validatePwaEntryResilienceOptions(options: PwaEntryResilienceOptions): PwaEntryResilienceValidatedOptions;
|
|
31
|
+
//# sourceMappingURL=options.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../../src/vite/options.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAElF,OAAO,KAAK,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAEpF,MAAM,MAAM,yBAAyB,GAAG;IACtC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,oCAAoC;IACpC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;mFAG+E;IAC/E,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB;6BACyB;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,kBAAkB,CAAC;IACrC,yFAAyF;IACzF,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;CACnD,CAAC;AAEF,MAAM,MAAM,kCAAkC,GAAG;IAC/C,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,SAAS,CAAC;CAC9D,CAAC;AAaF;iEACiE;AACjE,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,mBAAmB,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,KAAK,CAG/G;AAsGD,uGAAuG;AACvG,wBAAgB,iCAAiC,CAC/C,OAAO,EAAE,yBAAyB,GACjC,kCAAkC,CAapC"}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { ENTRY_PAGE_MESSAGE_KEYS } from "../page/messages.js";
|
|
2
|
+
const DEFAULT_MAX_VALIDITY_DAYS = 30;
|
|
3
|
+
const MIN_MAX_VALIDITY_DAYS = 1;
|
|
4
|
+
const MAX_MAX_VALIDITY_DAYS = 90;
|
|
5
|
+
/** Options ADR-0033 (2026-09-23) removed along with the package's cryptographic trust root: `keys` and `seed`
|
|
6
|
+
* backed the build-time signature verification, `approvedOrigins` was the Origin allow-list, `discoveryUrl`
|
|
7
|
+
* pointed at the discovery-source fetch. Rejected explicitly, not just left out of the type, because a caller
|
|
8
|
+
* without strict type-checking on `vite.config` (plain JS, a stale `.d.ts`, an `as any`) would otherwise have
|
|
9
|
+
* them silently ignored. */
|
|
10
|
+
const REMOVED_OPTION_NAMES = ["keys", "seed", "approvedOrigins", "discoveryUrl"];
|
|
11
|
+
/** Throws a `TypeError` naming a diagnostic code and path; never returns. `detail`, when given, is appended for a
|
|
12
|
+
* code whose fix isn't obvious from the code and path alone. */
|
|
13
|
+
export function failWithDiagnostic(code, path, detail) {
|
|
14
|
+
const message = `The pwaEntryResilience plugin's options are not valid: ${code} at ${path === "" ? "(root)" : path}`;
|
|
15
|
+
throw new TypeError(detail === undefined ? message : `${message} (${detail})`);
|
|
16
|
+
}
|
|
17
|
+
/** Throws for any of `REMOVED_OPTION_NAMES` present on `options`, naming ADR-0033 in the message. */
|
|
18
|
+
function checkForRemovedOptions(options) {
|
|
19
|
+
for (const name of REMOVED_OPTION_NAMES) {
|
|
20
|
+
if (name in options) {
|
|
21
|
+
failWithDiagnostic("entry.option-removed", `/${name}`, `"${name}" was removed by ADR-0033 — see docs/adr/0033-entry-manifest-supplied-by-the-application.md`);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function validateMaxValidityDays(value) {
|
|
26
|
+
if (value === undefined)
|
|
27
|
+
return DEFAULT_MAX_VALIDITY_DAYS;
|
|
28
|
+
if (typeof value !== "number" ||
|
|
29
|
+
!Number.isInteger(value) ||
|
|
30
|
+
value < MIN_MAX_VALIDITY_DAYS ||
|
|
31
|
+
value > MAX_MAX_VALIDITY_DAYS) {
|
|
32
|
+
failWithDiagnostic("entry.max-validity-days-invalid", "/maxValidityDays");
|
|
33
|
+
}
|
|
34
|
+
return value;
|
|
35
|
+
}
|
|
36
|
+
/** `undefined` when the option is absent; otherwise the string, rejected if it is not a string at all or contains
|
|
37
|
+
* `</style` (spec's "插件只做一处安全校验"). */
|
|
38
|
+
function validateCss(value) {
|
|
39
|
+
if (value === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
// Case-insensitive: the HTML tokenizer lowercases end-tag names, so `</STYLE` closes the block just as well.
|
|
42
|
+
if (typeof value !== "string" || /<\/style/i.test(value)) {
|
|
43
|
+
failWithDiagnostic("entry.css-invalid", "/css");
|
|
44
|
+
}
|
|
45
|
+
// Line endings normalized before the text is written or hashed: the HTML parser turns CRLF and lone CR into LF,
|
|
46
|
+
// and CSP hashes the normalized text, so a CRLF file would otherwise log a hash no browser would match.
|
|
47
|
+
return value.replace(/\r\n?/g, "\n");
|
|
48
|
+
}
|
|
49
|
+
const LOCALES = ["zh-CN", "en"];
|
|
50
|
+
const MAX_MESSAGE_LENGTH = 200;
|
|
51
|
+
/** `undefined` when the option is absent; otherwise the locale, rejected if it is not one of `LOCALES`. */
|
|
52
|
+
function validateLocale(value) {
|
|
53
|
+
if (value === undefined)
|
|
54
|
+
return "zh-CN";
|
|
55
|
+
if (typeof value !== "string" || !LOCALES.includes(value)) {
|
|
56
|
+
failWithDiagnostic("entry.locale-invalid", "/locale");
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
/** Number of non-overlapping occurrences of `token` in `text`. */
|
|
61
|
+
function countOccurrences(text, token) {
|
|
62
|
+
let count = 0;
|
|
63
|
+
let index = 0;
|
|
64
|
+
for (;;) {
|
|
65
|
+
const found = text.indexOf(token, index);
|
|
66
|
+
if (found === -1)
|
|
67
|
+
break;
|
|
68
|
+
count += 1;
|
|
69
|
+
index = found + token.length;
|
|
70
|
+
}
|
|
71
|
+
return count;
|
|
72
|
+
}
|
|
73
|
+
/** Validates one already-known message key's value; throws `entry.message-invalid` at `/messages/<key>` for
|
|
74
|
+
* anything a build would reject — never a string, empty, too long, or (for `expiry`/`go`) missing or duplicating
|
|
75
|
+
* its required placeholder. Never echoes the value itself, per this package's "never echo input" rule. */
|
|
76
|
+
function validateMessageValue(key, value) {
|
|
77
|
+
const path = `/messages/${key}`;
|
|
78
|
+
if (typeof value !== "string" || value.length === 0 || value.length > MAX_MESSAGE_LENGTH) {
|
|
79
|
+
failWithDiagnostic("entry.message-invalid", path);
|
|
80
|
+
}
|
|
81
|
+
if (key === "expiry" && countOccurrences(value, "{expiresAt}") !== 1) {
|
|
82
|
+
failWithDiagnostic("entry.message-invalid", path, "must contain {expiresAt} exactly once");
|
|
83
|
+
}
|
|
84
|
+
if (key === "go" && countOccurrences(value, "{host}") !== 1) {
|
|
85
|
+
failWithDiagnostic("entry.message-invalid", path, "must contain {host} exactly once");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
/** `undefined` when the option is absent; otherwise the override object, rejected as a whole if it is not a plain
|
|
89
|
+
* object, and key by key for an unknown key (any key outside `ENTRY_PAGE_MESSAGE_KEYS`) or an invalid value. */
|
|
90
|
+
function validateMessages(value) {
|
|
91
|
+
if (value === undefined)
|
|
92
|
+
return undefined;
|
|
93
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
94
|
+
failWithDiagnostic("entry.message-invalid", "/messages");
|
|
95
|
+
}
|
|
96
|
+
const record = value;
|
|
97
|
+
for (const key of Object.keys(record)) {
|
|
98
|
+
// Reported at the object's own path: the unknown key name is the caller's input, and diagnostics never carry it.
|
|
99
|
+
if (!ENTRY_PAGE_MESSAGE_KEYS.includes(key)) {
|
|
100
|
+
failWithDiagnostic("entry.message-invalid", "/messages");
|
|
101
|
+
}
|
|
102
|
+
validateMessageValue(key, record[key]);
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
/** Validates and normalizes the plugin's options, throwing for anything a build would reject later. */
|
|
107
|
+
export function validatePwaEntryResilienceOptions(options) {
|
|
108
|
+
if (options === null || typeof options !== "object") {
|
|
109
|
+
throw new TypeError("The pwaEntryResilience plugin requires an options object");
|
|
110
|
+
}
|
|
111
|
+
checkForRemovedOptions(options);
|
|
112
|
+
const maxValidityDays = validateMaxValidityDays(options.maxValidityDays);
|
|
113
|
+
const css = validateCss(options.css);
|
|
114
|
+
const locale = validateLocale(options.locale);
|
|
115
|
+
const messages = validateMessages(options.messages);
|
|
116
|
+
return { identity: options.identity, maxValidityDays, css, locale, messages };
|
|
117
|
+
}
|
|
118
|
+
//# sourceMappingURL=options.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/vite/options.ts"],"names":[],"mappings":"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"}
|