vanilla-jet 1.5.4 → 1.5.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/CHANGELOG.md +9 -0
- package/framework/router.js +14 -4
- package/package.json +1 -1
- package/test/server.test.js +11 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,15 @@ All notable project changes are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format follows a structure inspired by Keep a Changelog and semantic versioning.
|
|
6
6
|
|
|
7
|
+
## [1.5.5] - 2026-06-28
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **Fingerprinted assets are now cached immutably.** Static requests carrying a `?v=` (the
|
|
12
|
+
`?v=size-mtime` fingerprint from `dipper.versionedUrl`) are served `Cache-Control: public,
|
|
13
|
+
max-age=31536000, immutable`; non-versioned assets keep `no-cache, must-revalidate`. This eliminates
|
|
14
|
+
per-asset revalidation round-trips for clients without the service worker (notably native WebViews).
|
|
15
|
+
|
|
7
16
|
## [1.5.4] - 2026-06-28
|
|
8
17
|
|
|
9
18
|
### Fixed
|
package/framework/router.js
CHANGED
|
@@ -139,7 +139,10 @@ class Router {
|
|
|
139
139
|
}
|
|
140
140
|
|
|
141
141
|
let metadata = staticFile.metadata;
|
|
142
|
-
|
|
142
|
+
// Fingerprinted assets (requested with ?v=size-mtime) are safe to cache forever:
|
|
143
|
+
// any content change produces a new URL. Everything else keeps revalidation.
|
|
144
|
+
let isImmutable = Boolean(request.get('v'));
|
|
145
|
+
let staticHeaders = obj.buildStaticHeaders(extHeader, staticCandidates, staticFile.contentEncoding, metadata, isImmutable);
|
|
143
146
|
|
|
144
147
|
if (obj.isNotModified(req, metadata)) {
|
|
145
148
|
let notModifiedHeaders = Object.assign({}, staticHeaders);
|
|
@@ -280,7 +283,7 @@ class Router {
|
|
|
280
283
|
return `${route}|${normalizedEncodings}`;
|
|
281
284
|
}
|
|
282
285
|
|
|
283
|
-
buildStaticHeaders(extHeader, candidates, contentEncoding, metadata) {
|
|
286
|
+
buildStaticHeaders(extHeader, candidates, contentEncoding, metadata, isImmutable) {
|
|
284
287
|
let staticHeaders = Object.assign({}, extHeader);
|
|
285
288
|
if (contentEncoding) {
|
|
286
289
|
staticHeaders['Content-Encoding'] = contentEncoding;
|
|
@@ -292,8 +295,15 @@ class Router {
|
|
|
292
295
|
staticHeaders['Content-Length'] = metadata.size;
|
|
293
296
|
staticHeaders['ETag'] = metadata.etag;
|
|
294
297
|
staticHeaders['Last-Modified'] = metadata.lastModified;
|
|
295
|
-
|
|
296
|
-
|
|
298
|
+
if (isImmutable) {
|
|
299
|
+
// Fingerprinted URL (?v=): cache for a year and skip revalidation entirely.
|
|
300
|
+
// This is the big win for clients without the service worker (e.g. native WebViews),
|
|
301
|
+
// which otherwise revalidate every asset on every load.
|
|
302
|
+
staticHeaders['Cache-Control'] = 'public, max-age=31536000, immutable';
|
|
303
|
+
} else {
|
|
304
|
+
// Non-versioned assets: force revalidation to keep clients fresh without a hard reload.
|
|
305
|
+
staticHeaders['Cache-Control'] = 'no-cache, must-revalidate';
|
|
306
|
+
}
|
|
297
307
|
return staticHeaders;
|
|
298
308
|
}
|
|
299
309
|
|
package/package.json
CHANGED
package/test/server.test.js
CHANGED
|
@@ -78,6 +78,17 @@ test('static asset is served with caching validators', async () => {
|
|
|
78
78
|
assert.ok(res.headers.get('last-modified'), 'Last-Modified should be present');
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
test('fingerprinted (?v=) asset is cached immutable; plain asset revalidates', async () => {
|
|
82
|
+
const versioned = await fetch(`${baseUrl}/public/scripts/test.min.js?v=123-456`);
|
|
83
|
+
await versioned.arrayBuffer();
|
|
84
|
+
assert.equal(versioned.status, 200);
|
|
85
|
+
assert.match(versioned.headers.get('cache-control') || '', /immutable/);
|
|
86
|
+
|
|
87
|
+
const plain = await fetch(`${baseUrl}/public/scripts/test.min.js`);
|
|
88
|
+
await plain.arrayBuffer();
|
|
89
|
+
assert.match(plain.headers.get('cache-control') || '', /no-cache/);
|
|
90
|
+
});
|
|
91
|
+
|
|
81
92
|
test('conditional request returns 304 when ETag matches', async () => {
|
|
82
93
|
const first = await fetch(`${baseUrl}/public/scripts/test.min.js`);
|
|
83
94
|
const etag = first.headers.get('etag');
|