Document runtime exclusions and verify offline transfer savings
This commit is contained in:
@@ -335,3 +335,19 @@ No product behavior changes are involved in these measurement corrections.
|
|||||||
Keep browser/build/test processes idle during full timing measurements. WebKit's
|
Keep browser/build/test processes idle during full timing measurements. WebKit's
|
||||||
zero long-task field means unavailable, not zero work. Autoplay-blocked/media-ready
|
zero long-task field means unavailable, not zero work. Autoplay-blocked/media-ready
|
||||||
null results do not establish playback performance or iPhone audio continuity.
|
null results do not establish playback performance or iPhone audio continuity.
|
||||||
|
|
||||||
|
## Runtime manifest hygiene
|
||||||
|
|
||||||
|
`server/asset-manifest.js` excludes unit tests (`*.test.js`, `*.test.mjs`),
|
||||||
|
Markdown, text licences/data (`*.txt`), C build sources, `*.entry.js` build
|
||||||
|
entries, `/admin.html`, and files below any `perf/` directory from the runtime
|
||||||
|
manifest and both build-tag modes. They remain directly servable with the usual
|
||||||
|
hash and cache headers. If a matching file is genuinely needed at runtime, add
|
||||||
|
its exact root-relative path to the optional `include` array in
|
||||||
|
`frontend/assets.json`; group declarations referencing excluded files fail at boot
|
||||||
|
unless explicitly included.
|
||||||
|
|
||||||
|
The offline completeness harness compares cache contents with the runtime
|
||||||
|
manifest dynamically; it must not hard-code the old 151-file inventory. See
|
||||||
|
`plans/manifest-hygiene-report.md` and `perf/results/hygiene-*.json` for the
|
||||||
|
before/after measurements and compatibility checks.
|
||||||
|
|||||||
2893
perf/results/hygiene-after.json
Normal file
2893
perf/results/hygiene-after.json
Normal file
File diff suppressed because it is too large
Load Diff
260
perf/results/hygiene-assets.json
Normal file
260
perf/results/hygiene-assets.json
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
{
|
||||||
|
"before": {
|
||||||
|
"count": 151,
|
||||||
|
"rawBytes": 2044337,
|
||||||
|
"manifestBytes": 15601
|
||||||
|
},
|
||||||
|
"after": {
|
||||||
|
"count": 102,
|
||||||
|
"rawBytes": 1807059,
|
||||||
|
"manifestBytes": 12551
|
||||||
|
},
|
||||||
|
"excluded": [
|
||||||
|
{
|
||||||
|
"path": "/admin.html",
|
||||||
|
"rawBytes": 102005,
|
||||||
|
"brBytes": 24050
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/app-seams.test.js",
|
||||||
|
"rawBytes": 9786,
|
||||||
|
"brBytes": 2457
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/asset-sync-core.test.js",
|
||||||
|
"rawBytes": 9430,
|
||||||
|
"brBytes": 2525
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/asset-worker.test.js",
|
||||||
|
"rawBytes": 8219,
|
||||||
|
"brBytes": 1984
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/async-guard.test.js",
|
||||||
|
"rawBytes": 2543,
|
||||||
|
"brBytes": 765
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/audio-continuity.test.js",
|
||||||
|
"rawBytes": 2899,
|
||||||
|
"brBytes": 813
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/bento-hub.test.js",
|
||||||
|
"rawBytes": 868,
|
||||||
|
"brBytes": 283
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/car-mode.test.js",
|
||||||
|
"rawBytes": 2556,
|
||||||
|
"brBytes": 973
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/direct-protocol.test.js",
|
||||||
|
"rawBytes": 1279,
|
||||||
|
"brBytes": 521
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/direct-stream.test.js",
|
||||||
|
"rawBytes": 891,
|
||||||
|
"brBytes": 390
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/download-errors.test.js",
|
||||||
|
"rawBytes": 1783,
|
||||||
|
"brBytes": 566
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/downloads-page.test.js",
|
||||||
|
"rawBytes": 2453,
|
||||||
|
"brBytes": 699
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/downloads.test.js",
|
||||||
|
"rawBytes": 1608,
|
||||||
|
"brBytes": 639
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/eq-core.test.js",
|
||||||
|
"rawBytes": 1055,
|
||||||
|
"brBytes": 428
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/export.test.js",
|
||||||
|
"rawBytes": 521,
|
||||||
|
"brBytes": 259
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/f7-layout.test.js",
|
||||||
|
"rawBytes": 1400,
|
||||||
|
"brBytes": 380
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/fonts/PlusJakartaSans-OFL.txt",
|
||||||
|
"rawBytes": 4402,
|
||||||
|
"brBytes": 1428
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/fullscreen-orientation.test.js",
|
||||||
|
"rawBytes": 1278,
|
||||||
|
"brBytes": 404
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/glass-controls.test.js",
|
||||||
|
"rawBytes": 1177,
|
||||||
|
"brBytes": 412
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/glass-panel-layout.test.js",
|
||||||
|
"rawBytes": 782,
|
||||||
|
"brBytes": 259
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lazy.test.js",
|
||||||
|
"rawBytes": 7117,
|
||||||
|
"brBytes": 2030
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/loudness.test.js",
|
||||||
|
"rawBytes": 1626,
|
||||||
|
"brBytes": 706
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lower-third.test.js",
|
||||||
|
"rawBytes": 845,
|
||||||
|
"brBytes": 338
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lyrics-core.test.js",
|
||||||
|
"rawBytes": 4996,
|
||||||
|
"brBytes": 1430
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lyrics-display.test.js",
|
||||||
|
"rawBytes": 494,
|
||||||
|
"brBytes": 244
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/lyrics-window.test.js",
|
||||||
|
"rawBytes": 617,
|
||||||
|
"brBytes": 264
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/midi.test.js",
|
||||||
|
"rawBytes": 987,
|
||||||
|
"brBytes": 348
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/p2p-core.test.js",
|
||||||
|
"rawBytes": 2116,
|
||||||
|
"brBytes": 675
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/party-dj.test.js",
|
||||||
|
"rawBytes": 550,
|
||||||
|
"brBytes": 234
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/piano-core.test.js",
|
||||||
|
"rawBytes": 2102,
|
||||||
|
"brBytes": 731
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/prewarm-next.test.js",
|
||||||
|
"rawBytes": 1268,
|
||||||
|
"brBytes": 434
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/related-videos.test.js",
|
||||||
|
"rawBytes": 1514,
|
||||||
|
"brBytes": 556
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/resume-core.test.js",
|
||||||
|
"rawBytes": 1531,
|
||||||
|
"brBytes": 484
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/saved-page.test.js",
|
||||||
|
"rawBytes": 553,
|
||||||
|
"brBytes": 224
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/server-backfill.test.js",
|
||||||
|
"rawBytes": 2118,
|
||||||
|
"brBytes": 683
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/setlist-import.test.js",
|
||||||
|
"rawBytes": 1134,
|
||||||
|
"brBytes": 508
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/settings-sections.test.js",
|
||||||
|
"rawBytes": 1209,
|
||||||
|
"brBytes": 405
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/sha256.test.js",
|
||||||
|
"rawBytes": 3251,
|
||||||
|
"brBytes": 1153
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/shell-consistency.test.js",
|
||||||
|
"rawBytes": 5265,
|
||||||
|
"brBytes": 1507
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/stats-core.test.js",
|
||||||
|
"rawBytes": 3165,
|
||||||
|
"brBytes": 929
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/sw-update.test.js",
|
||||||
|
"rawBytes": 17825,
|
||||||
|
"brBytes": 4099
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/sw.test.js",
|
||||||
|
"rawBytes": 6219,
|
||||||
|
"brBytes": 1849
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/vendor/framework7-swipe.entry.js",
|
||||||
|
"rawBytes": 1040,
|
||||||
|
"brBytes": 290
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/video-edit.test.js",
|
||||||
|
"rawBytes": 3396,
|
||||||
|
"brBytes": 855
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/visible-timer.test.js",
|
||||||
|
"rawBytes": 2827,
|
||||||
|
"brBytes": 687
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/vocal-reducer.test.js",
|
||||||
|
"rawBytes": 648,
|
||||||
|
"brBytes": 235
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/wasm/loudness.c",
|
||||||
|
"rawBytes": 2132,
|
||||||
|
"brBytes": 861
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/wasm/sha256.c",
|
||||||
|
"rawBytes": 2428,
|
||||||
|
"brBytes": 1064
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/worker-imports.test.js",
|
||||||
|
"rawBytes": 1370,
|
||||||
|
"brBytes": 636
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"excludedBrBytes": 64694
|
||||||
|
}
|
||||||
5013
perf/results/hygiene-before-rollback-offline.json
Normal file
5013
perf/results/hygiene-before-rollback-offline.json
Normal file
File diff suppressed because it is too large
Load Diff
3383
perf/results/hygiene-before.json
Normal file
3383
perf/results/hygiene-before.json
Normal file
File diff suppressed because it is too large
Load Diff
41
perf/results/hygiene-migration.json
Normal file
41
perf/results/hygiene-migration.json
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"commit": "b77938a",
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"browser": "chromium",
|
||||||
|
"oldTag": "1175f1a1d2c1",
|
||||||
|
"newTag": "604f8f8d8a68",
|
||||||
|
"banners": 1,
|
||||||
|
"updateReloads": 1,
|
||||||
|
"cachedFiles": 124,
|
||||||
|
"durationMs": 26801,
|
||||||
|
"wireBytes": 638444,
|
||||||
|
"offline": true,
|
||||||
|
"legacyURLs": 21,
|
||||||
|
"profile": "lossy",
|
||||||
|
"userInitiatedOnly": true,
|
||||||
|
"playbackGuard": true,
|
||||||
|
"evictionRepair": true,
|
||||||
|
"incrementalRefresh": true,
|
||||||
|
"offlineTransportErrors": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"browser": "webkit",
|
||||||
|
"oldTag": "1175f1a1d2c1",
|
||||||
|
"newTag": "604f8f8d8a68",
|
||||||
|
"banners": 1,
|
||||||
|
"updateReloads": 1,
|
||||||
|
"cachedFiles": 124,
|
||||||
|
"durationMs": 24322,
|
||||||
|
"wireBytes": 743954,
|
||||||
|
"offline": true,
|
||||||
|
"legacyURLs": 21,
|
||||||
|
"profile": "lossy",
|
||||||
|
"userInitiatedOnly": true,
|
||||||
|
"playbackGuard": true,
|
||||||
|
"evictionRepair": true,
|
||||||
|
"incrementalRefresh": true,
|
||||||
|
"offlineTransportErrors": 4
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
5836
perf/results/hygiene-offline.json
Normal file
5836
perf/results/hygiene-offline.json
Normal file
File diff suppressed because it is too large
Load Diff
4817
perf/results/hygiene-rollback-offline.json
Normal file
4817
perf/results/hygiene-rollback-offline.json
Normal file
File diff suppressed because it is too large
Load Diff
150
plans/manifest-hygiene-report.md
Normal file
150
plans/manifest-hygiene-report.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
# Runtime manifest hygiene
|
||||||
|
|
||||||
|
Based on main/offline-fix tip `0b8e398`; offline-fix was already merged when this
|
||||||
|
worktree was created. Implementation: `dd09f66`. No frontend runtime, loading,
|
||||||
|
service-worker, playback or layout code changed.
|
||||||
|
|
||||||
|
## Policy and delivery
|
||||||
|
|
||||||
|
`server/asset-manifest.js` now selects runtime inputs separately from delivery
|
||||||
|
metadata. It excludes `*.test.js`, `*.test.mjs`, `*.md`, `*.txt`, `*.c`,
|
||||||
|
`*.entry.js`, `/admin.html`, and all files beneath a `perf/` directory. The
|
||||||
|
optional `assets.json` `include` array accepts exact root-relative existing file
|
||||||
|
paths to override this policy. An explicitly declared group cannot silently lose
|
||||||
|
an excluded dependency: boot rejects it unless the path is included.
|
||||||
|
|
||||||
|
The 49 excluded files here are 44 unit tests, admin HTML, the Plus Jakarta Sans
|
||||||
|
font licence, two WASM C build sources, and the Framework7 source build entry.
|
||||||
|
WASM binaries, fonts, icons, workers and the Framework7 runtime bundle remain.
|
||||||
|
|
||||||
|
Excluded files remain directly servable, with the existing content hash, ETag,
|
||||||
|
compression and cache rules: plain requests revalidate (font-folder requests
|
||||||
|
keep their 30-day cache rule), matching `?v` is immutable, and stale `?v` is
|
||||||
|
no-store in per-file mode. Delivery hashes are computed separately; they never
|
||||||
|
enter `manifest.files` or build identity. `ASSET_HASHING=0` retains single-tag
|
||||||
|
stamping and its legacy header matrix, but its tag also uses only runtime
|
||||||
|
inputs. `ASSET_SYNC=0` still selects the legacy worker.
|
||||||
|
|
||||||
|
Fresh installs cache only runtime assets. On an incremental upgrade, excluded
|
||||||
|
entries from the previous manifest may survive for one build under existing
|
||||||
|
N-1 retention, then are pruned as N-2. No new excluded entries are downloaded.
|
||||||
|
This preserves retention for already-open pages instead of changing worker
|
||||||
|
pruning or contracts/pinning.
|
||||||
|
|
||||||
|
## Inventory
|
||||||
|
|
||||||
|
Measured from the untouched before snapshot and the fixed frontend using the
|
||||||
|
respective real server manifest modules. Counts include stamped index and SW.
|
||||||
|
|
||||||
|
| Metric | Before | After | Reduction |
|
||||||
|
| --- | ---: | ---: | ---: |
|
||||||
|
| Manifest files | 151 | 102 | 49 (32.5%) |
|
||||||
|
| Summed manifest file sizes | 2,044,337 B | 1,807,059 B | 237,278 B |
|
||||||
|
| JSON manifest body | 15,601 B | 12,551 B | 3,050 B |
|
||||||
|
|
||||||
|
Excluded source bodies total 237,278 B, or 64,694 B using per-file Brotli
|
||||||
|
compression. Actual install/update measurements below include protocol and
|
||||||
|
manifest/index effects; compression estimates are not substituted for those runs.
|
||||||
|
Full inventory: `perf/results/hygiene-assets.json`.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
|
||||||
|
Commands:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
node --test frontend/*.test.js
|
||||||
|
cd server && bun install && bun run test
|
||||||
|
node perf/offline-complete.mjs --browser all --conditions --out perf/results/hygiene-offline.json
|
||||||
|
node perf/migration.mjs --browser all --profile lossy --out perf/results/hygiene-migration.json
|
||||||
|
node perf/offline-complete.mjs --browser all --legacy --out perf/results/hygiene-rollback-offline.json
|
||||||
|
```
|
||||||
|
|
||||||
|
The server test command used a worktree-local Python venv on PATH with yt-dlp
|
||||||
|
installed so both environment-sensitive worker tests ran successfully.
|
||||||
|
Frontend: 209 passed, zero failures. Server: 186 passed, zero failures.
|
||||||
|
Manifest/static/shell tests cover excluded-file identity invariance in both hash
|
||||||
|
modes, app.js identity changes, explicit inclusion, group validation, direct
|
||||||
|
bodies/hash/header behavior, SPA delivery and rollback flags.
|
||||||
|
|
||||||
|
Offline completeness: Chromium and WebKit each report manifest count 102,
|
||||||
|
missing `[]`, failures `[]`, across all four layouts and lazy features. Page
|
||||||
|
closure/interrupted completion, worker restart/re-registration, Save-Data, two
|
||||||
|
tabs and online-event resumption pass. Linux WebKit uses same-context worker
|
||||||
|
re-registration because persistent CacheStorage restart is broken there; its
|
||||||
|
offline proxy blocks transport because native setOffline breaks cached navigation.
|
||||||
|
Native OPFS-dependent video-edit behavior cannot be exercised in Linux WebKit.
|
||||||
|
|
||||||
|
Lossy legacy migration passes on both engines: one banner, one explicit-user
|
||||||
|
reload, offline launch, playback guard, legacy URLs, eviction repair and
|
||||||
|
incremental refresh. WebKit records four tolerated offline API transport errors,
|
||||||
|
with the offline assertions passing. Migration caches include retained N-1
|
||||||
|
entries, so their raw key count is deliberately greater than manifest count.
|
||||||
|
|
||||||
|
## Rollback limitation found during extra verification
|
||||||
|
|
||||||
|
An additional `--legacy` offline-completeness run disables both hashing and sync.
|
||||||
|
It fails on both the untouched base and fixed build: the legacy SHELL omits
|
||||||
|
`piano-engine.mjs`, so its direct offline request returns 503 across layouts.
|
||||||
|
The legacy shell also intentionally does not cache its own `sw.js`; the harness
|
||||||
|
therefore marks that inventory entry missing too, without a corresponding offline
|
||||||
|
UI failure. Before: 151 manifest entries / 51 missing; after: 102 / 2 missing.
|
||||||
|
The other 49 before-only misses are precisely the excluded non-runtime files.
|
||||||
|
Normal sync-mode completeness is 102/102 with no failures. Header/injection
|
||||||
|
rollback tests pass; this change neither fixes nor worsens the existing legacy
|
||||||
|
piano coverage gap. Results: `hygiene-before-rollback-offline.json` and
|
||||||
|
`hygiene-rollback-offline.json`.
|
||||||
|
|
||||||
|
## Reduced performance measurements
|
||||||
|
|
||||||
|
Three runs per scenario/engine, LTE. Before used an isolated untouched snapshot
|
||||||
|
of `0b8e398` inside this worktree; after used the fixed server. No external
|
||||||
|
worktree was modified. Cold wire totals include page requests and install/completion
|
||||||
|
fetches; these are not claimed to be worker-only bytes. Chromium uses CDP LTE
|
||||||
|
throttling; WebKit uses the LTE proxy.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
node perf/.tmp/before/perf/baseline.mjs --runs 3 --browser all --profile lte --scenario all --out perf/results/hygiene-before.json
|
||||||
|
node perf/baseline.mjs --runs 3 --browser all --profile lte --scenario all --out perf/results/hygiene-after.json
|
||||||
|
```
|
||||||
|
|
||||||
|
All values below are actual medians; bytes are wire bytes.
|
||||||
|
|
||||||
|
| Engine | Scenario | Before B | After B | Before ms | After ms |
|
||||||
|
| --- | --- | ---: | ---: | ---: | ---: |
|
||||||
|
| chromium | Cold visit + install | 728,266 | 660,390 | 7101 | 7090 |
|
||||||
|
| chromium | Warm reload | 8,132 | 8,159 | 647 | 445 |
|
||||||
|
| chromium | Offline reload | 0 | 0 | 285 | 209 |
|
||||||
|
| chromium | Core app.js edit | 126,358 | 125,729 | 1008 | 753 |
|
||||||
|
| chromium | theme-glass.css edit | 44,027 | 43,434 | 1600 | 1372 |
|
||||||
|
| chromium | Presenter edit + idle fill | 42,065 | 41,436 | 1392 | 1376 |
|
||||||
|
| webkit | Cold visit + install | 1,189,813 | 1,111,241 | 2321 | 2310 |
|
||||||
|
| webkit | Warm reload | 0 | 0 | 92 | 89 |
|
||||||
|
| webkit | Offline reload | 0 | 0 | 100 | 88 |
|
||||||
|
| webkit | Core app.js edit | 148,709 | 147,955 | 2013 | 2013 |
|
||||||
|
| webkit | theme-glass.css edit | 57,732 | 56,976 | 1479 | 1466 |
|
||||||
|
| webkit | Presenter edit + idle fill | 55,382 | 54,625 | 1494 | 1557 |
|
||||||
|
|
||||||
|
Cold requests fall 165 → 116 (Chromium) and 209 → 160 (WebKit), exactly 49
|
||||||
|
fewer each. Cold wire savings are 67,876 B (9.3%) and 78,572 B (6.6%).
|
||||||
|
Ordinary runtime updates already shared unchanged files before this fix, so
|
||||||
|
their savings are mainly smaller manifest metadata: approximately 0.6–0.8 KB.
|
||||||
|
Test-only edits now leave the tag unchanged and trigger no build update.
|
||||||
|
Milliseconds for cold/warm/offline are boot-done; update milliseconds are completion
|
||||||
|
duration. These three-run timing differences are descriptive, not a performance
|
||||||
|
claim. Warm Chromium API traffic varies independently of the asset set.
|
||||||
|
|
||||||
|
Real output excerpts (after):
|
||||||
|
|
||||||
|
```text
|
||||||
|
run 3/3... done (660390 bytes, 116 reqs, boot=7090ms)
|
||||||
|
run 3/3... done (1111241 bytes, 160 reqs, boot=2310ms)
|
||||||
|
run 3/3... done (147955 bytes, 5 reqs, dur=2014ms)
|
||||||
|
run 3/3... done (56976 bytes, 9 reqs, dur=1483ms)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Real iPhone checks
|
||||||
|
|
||||||
|
Confirm installed-PWA offline launch and feature/layout first use after update;
|
||||||
|
completion after suspension, storage pressure/OPFS sharing, and old excluded
|
||||||
|
cache entries disappearing after the subsequent build. Linux automation cannot
|
||||||
|
prove iOS worker lifetime, quota eviction or native OPFS behavior.
|
||||||
@@ -55,7 +55,6 @@ export function createAssetManifest(publicDir = './public', { hashing = true, bu
|
|||||||
else {
|
else {
|
||||||
const content = readFileSync(path);
|
const content = readFileSync(path);
|
||||||
bytes.set(url, content);
|
bytes.set(url, content);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,7 +90,7 @@ export function createAssetManifest(publicDir = './public', { hashing = true, bu
|
|||||||
const swSource = bytes.get('/sw.js')?.toString() ?? null;
|
const swSource = bytes.get('/sw.js')?.toString() ?? null;
|
||||||
// Derived build metadata cannot be an input to its own hash. Canonicalize
|
// Derived build metadata cannot be an input to its own hash. Canonicalize
|
||||||
// the index meta and SW injected tag, then publish hashes of the final bytes.
|
// the index meta and SW injected tag, then publish hashes of the final bytes.
|
||||||
// All original bytes (including index/SW source) remain inputs via source hashes.
|
// All runtime source bytes (including index/SW source) remain inputs via source hashes.
|
||||||
let canonicalIndex = source === null ? null : stampIndex(embedAssets(source, files, groups, '__BUILD_TAG__', hashing), files, { hashing });
|
let canonicalIndex = source === null ? null : stampIndex(embedAssets(source, files, groups, '__BUILD_TAG__', hashing), files, { hashing });
|
||||||
const canonical = { assetSync, files: { ...files }, groups, contracts };
|
const canonical = { assetSync, files: { ...files }, groups, contracts };
|
||||||
if (canonicalIndex !== null && hashing) canonical.files['/index.html'] = { ...files['/index.html'], h: assetHash(canonicalIndex), s: Buffer.byteLength(canonicalIndex), source: files['/index.html'].h };
|
if (canonicalIndex !== null && hashing) canonical.files['/index.html'] = { ...files['/index.html'], h: assetHash(canonicalIndex), s: Buffer.byteLength(canonicalIndex), source: files['/index.html'].h };
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ test('non-runtime files stay deliverable but never affect manifest or build iden
|
|||||||
for (const hashing of [true, false]) {
|
for (const hashing of [true, false]) {
|
||||||
const first = createAssetManifest(dir, { hashing });
|
const first = createAssetManifest(dir, { hashing });
|
||||||
mkdirSync(join(dir, 'perf'), { recursive: true });
|
mkdirSync(join(dir, 'perf'), { recursive: true });
|
||||||
for (const name of ['app.test.js', 'app.test.mjs', 'README.md', 'license.txt', 'admin.html', 'perf/fixture.js']) {
|
for (const name of ['app.test.js', 'app.test.mjs', 'README.md', 'license.txt', 'admin.html', 'perf/fixture.js', 'sha256.c', 'framework.entry.js']) {
|
||||||
writeFileSync(join(dir, name), 'non-runtime');
|
writeFileSync(join(dir, name), 'non-runtime');
|
||||||
const next = createAssetManifest(dir, { hashing });
|
const next = createAssetManifest(dir, { hashing });
|
||||||
expect(next.manifest.buildTag).toBe(first.manifest.buildTag);
|
expect(next.manifest.buildTag).toBe(first.manifest.buildTag);
|
||||||
|
|||||||
Reference in New Issue
Block a user