perf: review-fixes (N+1, waterfalls, sanitize-cache, lazy strommix)
Deploy / verify (push) Successful in 56s
Deploy / deploy (push) Successful in 1m3s

- blog-utils.resolvePostOverviewBlocks: parallel getPostBySlug via
  Promise.all + .catch(null) instead of sequential await loop.
- blog-utils.resolveCalendarBlocks: same fix for calendar items.
- [...slug] page load: SearchableText/Calendar/DeadlineBanner now run
  parallel after tagsMap (was strict waterfall).
- sanitize-blocks: LRU cache keyed on raw html input — sanitize-html is
  ~ms/KB, the same content repeats across pages and cache hits.
- cms.getNormalizedTranslations: WeakMap-memoised the per-request
  Object.entries normalize loop on top of the existing 5-min bundle TTL.
- cms.cached: opt-in dev cache via DEV_CACHE_CMS=1 (default still off).
- rusty-image.ensureTransformedImage: was async-wrapping a sync URL
  builder; made sync, removed awaits at call sites — saves microtasks.
- BlockRenderer: lazy-load StrommixBlock (~220 KB) via dynamic import
  in the template — pages without the live-strommix widget no longer
  ship its bundle.

Also: enabled Caddy zstd+gzip on windwiderstand.de vhost (server-side
edit; verified `content-encoding: zstd` on response).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-05-21 12:30:15 +02:00
parent c00eb26689
commit 81c82c33e6
9 changed files with 144 additions and 91 deletions
+61 -61
View File
@@ -326,26 +326,22 @@ export async function loadPostsList(opts: {
for (const post of posts) {
const field = getPostImageField(post.postImage);
if (field) {
try {
const url = await ensureTransformedImage(field.url, {
width: 400,
height: 267,
fit: "cover",
format: "webp",
focalX: field.focal?.x,
focalY: field.focal?.y,
});
const p = post as PostEntry & {
_resolvedImageUrl?: string;
_rawImageUrl?: string;
_imageFocal?: { x: number; y: number } | null;
};
p._resolvedImageUrl = url;
p._rawImageUrl = field.url;
p._imageFocal = field.focal ?? null;
} catch {
/* image optional */
}
const url = ensureTransformedImage(field.url, {
width: 400,
height: 267,
fit: "cover",
format: "webp",
focalX: field.focal?.x,
focalY: field.focal?.y,
});
const p = post as PostEntry & {
_resolvedImageUrl?: string;
_rawImageUrl?: string;
_imageFocal?: { x: number; y: number } | null;
};
p._resolvedImageUrl = url;
p._rawImageUrl = field.url;
p._imageFocal = field.focal ?? null;
}
}
} catch (e) {
@@ -512,15 +508,14 @@ export async function resolvePostOverviewBlocks(
.filter(Boolean);
const limit =
typeof item.numberItems === "number" ? item.numberItems : 9999;
const resolved: PostEntry[] = [];
for (const slug of slugs.slice(0, limit)) {
const post = await getPostBySlug(slug, {
locale: "de",
resolve: ["all"],
});
if (post) resolved.push(post);
}
item.postsResolved = resolved;
const settled = await Promise.all(
slugs.slice(0, limit).map((slug) =>
getPostBySlug(slug, { locale: "de", resolve: ["all"] }).catch(
() => null,
),
),
);
item.postsResolved = settled.filter((p): p is PostEntry => !!p);
}
}
if (Array.isArray(item.postsResolved)) {
@@ -531,26 +526,22 @@ export async function resolvePostOverviewBlocks(
for (const post of item.postsResolved as PostEntry[]) {
const field = getPostImageField(post.postImage);
if (field) {
try {
const url = await ensureTransformedImage(field.url, {
width: 400,
height: 267,
fit: "cover",
format: "webp",
focalX: field.focal?.x,
focalY: field.focal?.y,
});
const p = post as PostEntry & {
_resolvedImageUrl?: string;
_rawImageUrl?: string;
_imageFocal?: { x: number; y: number } | null;
};
p._resolvedImageUrl = url;
p._rawImageUrl = field.url;
p._imageFocal = field.focal ?? null;
} catch {
// Bild optional
}
const url = ensureTransformedImage(field.url, {
width: 400,
height: 267,
fit: "cover",
format: "webp",
focalX: field.focal?.x,
focalY: field.focal?.y,
});
const p = post as PostEntry & {
_resolvedImageUrl?: string;
_rawImageUrl?: string;
_imageFocal?: { x: number; y: number } | null;
};
p._resolvedImageUrl = url;
p._rawImageUrl = field.url;
p._imageFocal = field.focal ?? null;
}
}
}
@@ -602,19 +593,28 @@ export async function resolveCalendarBlocks(
resolve: "items",
});
const raw = calendar?.items ?? rawItems;
const resolved: CalendarItemData[] = [];
for (const x of raw) {
if (typeof x === "object" && x !== null && "terminZeit" in x && "title" in x) {
resolved.push(x as CalendarItemData);
} else {
const islug = typeof x === "string" ? x : (x as { _slug?: string })?._slug;
if (islug) {
const entry = await getCalendarItemBySlug(islug, { locale: "de" });
if (entry) resolved.push(entry);
const settled = await Promise.all(
raw.map(async (x): Promise<CalendarItemData | null> => {
if (
typeof x === "object" &&
x !== null &&
"terminZeit" in x &&
"title" in x
) {
return x as CalendarItemData;
}
}
}
(item as { items?: CalendarItemData[] }).items = resolved;
const islug =
typeof x === "string" ? x : (x as { _slug?: string })?._slug;
if (!islug) return null;
const entry = await getCalendarItemBySlug(islug, { locale: "de" }).catch(
() => null,
);
return entry ?? null;
}),
);
(item as { items?: CalendarItemData[] }).items = settled.filter(
(e): e is CalendarItemData => !!e,
);
}
}
}