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
+10 -3
View File
@@ -51,11 +51,18 @@ export const load: PageServerLoad = async ({ params, locals, fetch, url, setHead
error(404, 'Seite nicht gefunden');
}
// resolveContentImages baut nur URLs zusammen (kein I/O) und mutiert den
// Tree — vor den Block-Resolvern erledigen, die danach gegen mutierte
// Blocks arbeiten.
await resolveContentImages(page);
// PostOverview liefert tagsMap, den SearchableText braucht. Calendar +
// DeadlineBanner sind unabhängig → parallel mit SearchableText laufen.
const tagsMap = await resolvePostOverviewBlocks(page);
await resolveSearchableTextBlocks(page, tagsMap);
await resolveCalendarBlocks(page);
await resolveDeadlineBannerBlocks(page);
await Promise.all([
resolveSearchableTextBlocks(page, tagsMap),
resolveCalendarBlocks(page),
resolveDeadlineBannerBlocks(page),
]);
sanitizeBlocks(page);
// Resolve top banner if present