Summary
Analytics now shows real engagement, closing the "publish counts only" gap.
- Driver:
postMetrics()fetches like/repost/reply counts viaapp.bsky.feed.getPosts(25 URIs per call; deleted posts skipped) - Service:
syncMetrics()batches the ~100 most recent published Bluesky targets and stores counts inpost_targets.metricswith a sync timestamp - Scheduling:
SyncEngagementMetricsjob every 30 minutes (silently no-ops when no account is connected), plusPOST /postline/metrics/sync(auth) for on-demand refresh - Analytics: new Engagement stat card — total interactions with a likes/reposts/replies breakdown; stat grid goes 4-up. The best-window histogram switches to
created_atsince metric syncs bumpupdated_atand would have skewed it.
Verification
bun test— 15 pass (2 new: getPosts query/count mapping incl. deleted-post absence; empty-input short-circuit)- Live: seeded metrics aggregate correctly through the API (523 likes · 62 reposts · 36 replies across 3 synced targets) and render on the page while authenticated; the sync endpoint fails cleanly without Bluesky credentials
pickierclean,tsc --noEmitexit 0