The INP observer sorted Object.values(interactions) on every event entry,
even though metrics.inp is only read when sendPerformance() flushes.
Defer the sort + p98 computation to flush, and drain queued observer
entries via observer.takeRecords() to capture the most recent
interactions on pagehide/visibilitychange.
Output is identical (same INP value computed). Savings are largest on
interaction-heavy pages and low-end devices where the per-event sort
compounds.
Address Greptile review feedback on #4243.
- Cloud-mode link.updateMany / pixel.updateMany now filter where: { ..., deletedAt: null } so a previously soft-deleted row keeps its original deletion timestamp instead of being restamped with the current time.
- Pre-transaction findMany now selects deletedAt; the Redis invalidation list filters to only live slugs, avoiding harmless but wasted DEL calls for already-soft-deleted entries.
Note: the share.deleteMany cleanup still uses the broad entityId list (not filtered by deletedAt) so that orphan share rows of already-soft-deleted links/pixels are still cleaned up. Filtering the prefetch itself, as Greptile's exact suggestion proposed, would skip those shares while link.deleteMany still hard-deletes the rows, leaving orphan share rows behind. Verified empirically with a 3-scenario reproduction.
deleteUser and deleteTeam left link/pixel/board rows (and their share rows)
in the database after the owner was removed. /q/<slug> and /p/<slug>
also kept serving deleted entries because the routes did not filter
deletedAt and Redis cached lookups for 24h.
- deleteUser: clean up link/pixel/board + shares for the deleted user.
Cloud mode: soft-delete link/pixel, hard-delete board, only userId-owned.
Non-cloud: hard-delete everything matching userId or owned teamIds.
- deleteTeam: same cleanup, scoped to teamId.
- /q and /p route handlers: filter deletedAt: null at the call sites
(not in findLink/findPixel helpers, which would null-deref the
permission checks at src/permissions/link.ts and pixel.ts).
- Post-transaction Redis invalidation mirrors deleteWebsite.
The handlePush function was still calling track() on SPA navigations
regardless of the auto-pageview setting. Gate that call behind the
autoPageview flag while keeping URL state and performance flush active.
Extracts click events from rrweb session-replay buffers at write time
into a new heatmap_event table (Prisma + ClickHouse). New /heatmaps
report shows a page picker plus an overlay of click density on the
dominant viewport, with an optional iframe of the live page behind.