feedkeeper 0.28.7 → 0.28.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "feedkeeper",
3
- "version": "0.28.7",
3
+ "version": "0.28.8",
4
4
  "description": "A lightweight, self-hosted RSS reader and remote MCP server for your news, blogs, and AI workflows.",
5
5
  "keywords": [
6
6
  "rss",
@@ -9,7 +9,7 @@ import { loadFullText } from "./fullText.js";
9
9
  import { canAccessItem, findItemById, findItemForUser } from "./repository.js";
10
10
  import { decodeEntities } from "./text.js";
11
11
  import { ImageCache, ImageCacheBusyError } from "./imageCache.js";
12
- import { detectImageType } from "./imageTypes.js";
12
+ import { detectImageType, isVectorImageUrl } from "./imageTypes.js";
13
13
  export { detectImageType } from "./imageTypes.js";
14
14
  import { comparableImageKey } from "./heroImage.js";
15
15
  const MAX_IMAGES_PER_ARTICLE = 30;
@@ -325,7 +325,7 @@ function proxiedImageUrl(origin, itemId, imageUrl, apiBase = "/api") {
325
325
  export function withProxiedImages(items, origin, apiBase = "/api", withContent = true) {
326
326
  const own = `${origin.replace(/\/$/, "")}/api/`;
327
327
  return items.map((item) => {
328
- const urls = new Set(itemImageUrls(item).filter((url) => !url.startsWith(own)));
328
+ const urls = new Set(itemImageUrls(item).filter((url) => !url.startsWith(own) && !isVectorImageUrl(url)));
329
329
  if (urls.size === 0)
330
330
  return item;
331
331
  const replace = (url) => (urls.has(url) ? proxiedImageUrl(origin, item.id, url, apiBase) : undefined);
@@ -13,4 +13,13 @@ export function detectImageType(data) {
13
13
  return { mime: "image/avif", ext: "avif" };
14
14
  return null;
15
15
  }
16
+ /** Vector images are never served by the image proxy, so their addresses stay as the publisher wrote them. */
17
+ export function isVectorImageUrl(url) {
18
+ try {
19
+ return new URL(url).pathname.toLowerCase().endsWith(".svg");
20
+ }
21
+ catch {
22
+ return false;
23
+ }
24
+ }
16
25
  //# sourceMappingURL=imageTypes.js.map
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@feedkeeper/server",
3
- "version": "0.28.7",
3
+ "version": "0.28.8",
4
4
  "private": true,
5
5
  "type": "module"
6
6
  }
package/web/dist/sw.js CHANGED
@@ -173,12 +173,17 @@ async function mutableImage(request) {
173
173
  } catch { return generation === privateGeneration ? cached ?? Response.error() : Response.error(); }
174
174
  }
175
175
 
176
- async function networkFirst(cacheName, request, limit) {
176
+ async function networkFirst(cacheName, request, limit, retried = false) {
177
177
  let generation = privateGeneration;
178
178
  let cache = await caches.open(cacheName);
179
179
  try {
180
180
  const response = await withTimeout(fetch(request), NETWORK_TIMEOUT_MS);
181
- if (generation !== privateGeneration) throw new Error("session_changed");
181
+ if (generation !== privateGeneration) {
182
+ // Parallel requests during a login race with the cache reset. The answer may belong to the old
183
+ // session, so it is neither cached nor returned; asking again must not look like a lost connection.
184
+ if (!retried) return await networkFirst(cacheName, request, limit, true);
185
+ throw new Error("session_changed");
186
+ }
182
187
  if (new URL(request.url).pathname === "/api/auth/me" && generation === privateGeneration) {
183
188
  if (response.ok) {
184
189
  const user = await response.clone().json();