@ultimat3/pwa 22.3.3 → 22.3.5
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/CLAUDE.md +1 -1
- package/package.json +3 -3
- package/src/service-worker.ts +13 -11
package/CLAUDE.md
CHANGED
|
@@ -23,7 +23,7 @@ Tier 4. May import tiers 0–3: `core`, `schema`, `i18n`, `money`, `time`, `cach
|
|
|
23
23
|
| A truncation of author text | CODE POINTS, never `slice`. `short_name` falls back to the first 12 of `name`, and a UTF-16 `slice` cuts inside a surrogate pair — the lone half encodes as U+FFFD, so an app named with an emoji got a home-screen label ending in a replacement character. `Array.from(name).slice(0, n).join('')` is the form. |
|
|
24
24
|
| Declared and never wired | `ServiceWorkerConfig.shellUrl` / `shellRevision` / `shellBytes` and `PwaRoute.dataUrl`. No caller in the framework's build path sets any of them, so `precache.ts`'s `reason: 'shell'` and `reason: 'route-data'` branches are reachable only from a hand-built `generateServiceWorker` call. Each doc comment now says so; the shell trio's said "precached for every `spa` route" while `spa` was already deleted from `RENDER_MODES`. **Kept, not deleted** — a public field is a major — and carried as candidates for the next major's declared-and-never-wired half. `revision` and `bytes` are the pair that IS fed, by the CLI's prerender pass. |
|
|
25
25
|
| Every byte count and every threshold | a whole number of 0 or more, screened with `finiteCount` where it is READ — `buildPrecacheManifest`'s `warnBytes` AND each entry's own `bytes` (shell, route, asset), `createInstallController`'s `minEngagementMs`, `retentionPlan`'s `keep`. `totalBytes > warnBytes` is false when EITHER side is `NaN`, so one asset whose byte count did not arrive as a number took down the install-size warning for every other entry, silently, in the function whose output is otherwise byte-identical per commit; `now() - startedAt < NaN` is false at every instant, so the install prompt fired on first paint; and `Math.max(1, NaN)` is `NaN` with `slice(0, NaN)` `[]`, so a retention plan evicted every deploy including the running one. `Math.max` is not a validator — it propagates. `keep: 0` still means 1 (`version-skew.test.ts` pins it), so the floor is 0 everywhere here. `bun run finite-bounds` is the ratchet. |
|
|
26
|
-
| Precache KEY | the bare URL, always. The revision addresses the FETCH (`?v=<hash>`), never the key: every strategy looks an entry up with `caches.match(req)` and `ignoreSearch` defaults to `false`, so an entry left keyed under `?v=` is a permanent miss — offline serves the fallback document instead of the precached page, and online every precached byte is downloaded twice. `
|
|
26
|
+
| Precache KEY | the bare URL, always. The revision addresses the FETCH (`?v=<hash>`), never the key: every strategy looks an entry up with `caches.match(req)` and `ignoreSearch` defaults to `false`, so an entry left keyed under `?v=` is a permanent miss — offline serves the fallback document instead of the precached page, and online every precached byte is downloaded twice. Each entry is fetched and `put` on its own, `As of 22.3.4`: `addAll`'s all-or-nothing let one 404 (a stylesheet another pod minted) fail the install of every deploy, so every visitor stayed on the first worker that ever installed (notificado.co). A failed entry costs its offline copy only. `service-worker-runtime.test.ts` executes the emitted `sw.js` against stub `caches`/`fetch` rather than asserting its text. |
|
|
27
27
|
| Precache FETCH | the revision is appended with `?` or `&`, picked per entry. `PrecacheAsset.url` is public API and a bundler emits `?v=<hash>` of its own, so a fixed `?` built `...?locale=en?v=<rev>`; `addAll` is all-or-nothing, so one non-200 there means `install` rejects and the worker never activates at all. |
|
|
28
28
|
| SW scope | `assertScope` refuses a relative `swPath`. `lastIndexOf('/')` on `sw.js` is `-1`, so the directory was `''` and `scope.startsWith('')` passed for every scope — the check waved through exactly the config most likely to be wrong. |
|
|
29
29
|
| HTML sinks | one escaper: `escapeAttribute` from `@ultimat3/seo` (tier 1, and the only one reachable — render's `html.ts` is tier 4, sideways). `appleTouchLinks` and `renderThemeColorMeta` interpolate app config into attributes; both escape. Never a second escaper here. |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultimat3/pwa",
|
|
3
|
-
"version": "22.3.
|
|
3
|
+
"version": "22.3.5",
|
|
4
4
|
"description": "Generated service worker, web manifest, icons, push and version-skew handling.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"test": "bun test"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@ultimat3/core": "22.3.
|
|
36
|
-
"@ultimat3/seo": "22.3.
|
|
35
|
+
"@ultimat3/core": "22.3.5",
|
|
36
|
+
"@ultimat3/seo": "22.3.5"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/service-worker.ts
CHANGED
|
@@ -236,13 +236,13 @@ function serializeRules(rules: readonly RouteRule[]): string {
|
|
|
236
236
|
* `caches.match(req)` on the bare URL — `ignoreSearch` defaults to `false` — so an entry left
|
|
237
237
|
* keyed under `?v=<revision>` is a permanent miss: offline serves the fallback document instead
|
|
238
238
|
* of the page that was precached, and online every precached byte is downloaded a second time.
|
|
239
|
-
*
|
|
240
|
-
* half-populated precache from activating
|
|
239
|
+
* Each entry is fetched and stored on its own, `As of 22.3.4`: `addAll`'s all-or-nothing was meant
|
|
240
|
+
* to stop a half-populated precache from activating, and instead one 404 blocked every install
|
|
241
|
+
* of every deploy (notificado.co, 22.3.2) — a stuck worker is worse than a missing offline copy.
|
|
241
242
|
*
|
|
242
243
|
* The separator in front of `v=` is chosen per entry, because `PrecacheAsset.url` is public API
|
|
243
244
|
* and a bundler emits a query of its own: a fixed `?` built `...?locale=en?v=<rev>`, and a single
|
|
244
|
-
* non-200 for it
|
|
245
|
-
* no version-skew header, and a worker that never activates at all.
|
|
245
|
+
* non-200 for it cost that entry its offline copy.
|
|
246
246
|
*
|
|
247
247
|
* `skipWaiting()` FIRST, `As of 22.3.2`: a new worker activates as soon as its precache is filled,
|
|
248
248
|
* never "once every tab of the origin is closed", which for a returning visitor was days. Nothing
|
|
@@ -259,15 +259,17 @@ self.addEventListener('install',(event)=>{
|
|
|
259
259
|
self.skipWaiting();
|
|
260
260
|
event.waitUntil((async()=>{
|
|
261
261
|
const cache=await caches.open(PRECACHE);
|
|
262
|
+
// ONE ENTRY AT A TIME, and a failure costs that entry only. addAll was all-or-nothing: one
|
|
263
|
+
// asset answering 404 rejected the install on every deploy, and every visitor stayed on the
|
|
264
|
+
// first worker that ever installed. A missing entry is fetched from the network when asked for.
|
|
262
265
|
// Revision is a content hash: unchanged assets are not re-downloaded across deploys.
|
|
263
266
|
// The separator is picked per entry: a precache URL may already carry a query.
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
267
|
+
await Promise.all(PRECACHE_MANIFEST.map(async(e)=>{
|
|
268
|
+
try{
|
|
269
|
+
const r=await fetch(new Request(e.url+(e.url.indexOf('?')<0?'?':'&')+'v='+e.revision,{cache:'reload'}));
|
|
270
|
+
if(r.ok)await cache.put(new Request(e.url),r);
|
|
271
|
+
}catch(err){}
|
|
272
|
+
}));
|
|
271
273
|
})());
|
|
272
274
|
});`.trim();
|
|
273
275
|
|