Add PostHog error tracking, richer telemetry, docs analytics, and release annotations#86
Conversation
…d CI context - captureException sends sanitized $exception events for PostHog error tracking: package-relative stack frames, scrubbed messages (emails, URLs, quoted text, tokens, home dirs), per-process dedupe and a 5-report cap - tag client created / email sent / cli command run with source (sdk|cli); the CLI reports unexpected crashes as unhandled exceptions - new email batch sent summary event alongside per-item events - detect ci_vendor (GitHub Actions, GitLab, CircleCI, Jenkins, Travis, Buildkite, Vercel, generic) and derive ci from it - disclose error reports in the notice and both READMEs; add changeset Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
…dk.dev - posthog-js init on hydration with history-change pageviews, exception autocapture, and web vitals; session replay stays off (one-line flip) - keep Vercel Analytics alongside - allow https://*.posthog.com in the CSP script-src and connect-src - disclose PostHog and package telemetry in the privacy policy Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
Posts a project annotation for @opencoredev/email-sdk releases so version cohorts are visible on dashboards. Skips gracefully when the POSTHOG_PERSONAL_API_KEY secret is absent and never fails the release. Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds PostHog error tracking, enriched telemetry (source tagging, CI detection, batch summaries), browser analytics for the docs site, and a release annotation step to the publish workflow. The core new surface is
Confidence Score: 5/5Safe to merge — all new telemetry paths are fire-and-forget with comprehensive try/catch guards, existing opt-outs are unchanged, and the privacy sanitization is well-tested. The error tracking implementation is thorough: hostile error shapes (throwing getters, non-Error throws, circular cause chains) are all handled, deduplication prevents spamming PostHog, and every redaction rule has explicit test coverage. The core send/sendBatch paths are unchanged in behavior — telemetry is always void-and-ignored. The only new observable side-effects are the PostHog HTTP calls, bounded by the 3s timeout and a 5-report-per-process cap. No files require special attention for merge safety. The minor observations in core.ts (empty-batch event), telemetry.ts (dedup classKey design), and the workflow step (version guard) are all non-blocking. Important Files Changed
Sequence DiagramsequenceDiagram
participant CLI as cli.ts
participant Core as core.ts (send/sendBatch)
participant Tel as telemetry.ts
participant PH as PostHog API
CLI->>Core: "createEmailClient({telemetrySource:"cli"})"
Core->>Tel: "capture("client created", {source:"cli"})"
Tel-->>PH: POST /capture/ (queued)
CLI->>Core: client.send(message)
Core->>Core: sendWithAdapters()
alt success
Core->>Tel: "capture("email sent", {success:true, source})"
else provider failure (isReportableSendError)
Core->>Tel: "capture("email sent", {success:false})"
Core->>Tel: "captureException(error, {handled:true})"
Tel->>Tel: buildExceptionList() → redactErrorMessage() + parseStackFrames()
Tel->>Tel: dedup check (seenObjects, seenClasses, budget)
Tel-->>PH: POST /capture/ ($exception event)
end
CLI->>Tel: "capture("cli command run", {source:"cli"})"
Note over CLI,Tel: captureCliRun() → flush()
Tel->>Tel: flush() — await all pending
alt unhandled crash (not CliFailure/EmailSdkError)
CLI->>Tel: "captureException(error, {handled:false})"
Tel-->>PH: POST /capture/ ($exception)
end
Note over PH: Release workflow
CLI->>PH: POST /api/projects/468042/annotations/ (vX.Y.Z released)
Reviews (7): Last reviewed commit: "fix(telemetry): stop tagging Vercel prod..." | Re-trigger Greptile |
Addresses review findings on the error-tracking change: - redact home directories before token matching so a long alphanumeric username collapses to "~" instead of leaking as "/home/<token>" - guard captureException against hostile error shapes: ignore non-string stack values and wrap the build path so a throwing stack getter can never make reporting throw - build the PostHog annotation body with jq so a crafted version string can't break out of the JSON or override the annotation scope Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
Greptile review follow-ups: - redact tokens with lookaround anchors instead of \b so base64 secrets ending in "=" padding are scrubbed whole rather than leaking the tail - mark an error object "seen" only after it passes the class and budget guards, so a bailed-out report never silently consumes a distinct error Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
… batch adapter Greptile review follow-ups (pass 2): - redact any scheme://… so SMTP/AMQP/DB connection strings with embedded credentials are scrubbed, and run URL redaction before email so a "scheme://user:pass@host" collapses whole - reduce project-relative stack frames (tsx/bun-from-source) to basenames so reports never leak the project directory layout; node: frames kept intact - report adapter "mixed" on email batch sent when items use different adapters instead of always showing the batch default Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
Greptile review follow-ups (pass 3): - pin the docs-site CSP to PostHog's actual hosts (us-assets.i.posthog.com for scripts, us.i.posthog.com + us-assets for connect) instead of the https://*.posthog.com wildcard, so only PostHog's known origins run/serve - record the adapter that actually delivered each batch item (response provider, which fallbacks change) rather than the intended primary, so the batch summary's adapter matches the per-item events Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
…paths Greptile review follow-ups (pass 4): - set posthog-js person_profiles: "identified_only" so anonymous docs traffic never creates person profiles, matching the SDK's server-side $process_person_profile: false and the privacy policy - extend the home-path redaction to match backslash separators so another user's Windows home path (\Users\name) is scrubbed, not just Unix paths Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
Greptile review follow-up (pass 5): VERCEL=1 is injected into Vercel's production serverless runtime, not just CI builds, so an SDK deployed in a Vercel function would report every live send as ci=true. Drop the VERCEL heuristic; Vercel CI builds still set CI=1 and resolve to "generic", while production runtimes correctly report no CI. Generated-By: PostHog Code Task-Id: da0ebd32-eea6-4c6f-aa27-9eff2d00808f
…annotations (#141) * feat(telemetry): anonymous usage analytics and redacted error tracking for SDK and CLI Replay of PRs #80 + #86 onto the new core (post batch recipient variables #125 and scheduled sends #127). - telemetry.ts / telemetry.test.ts ported byte-for-byte from #86, including the reviewed redaction pipeline, opt-outs (EMAIL_SDK_TELEMETRY=0/false/off, DO_NOT_TRACK, NODE_ENV=test, telemetry: false), first-run notice, exception dedupe/budget, and fire-and-forget delivery with a 3s timeout. - core.ts re-instruments the new send architecture: one "email sent" event per user-facing send() call (never per expanded recipient), with used_recipient_variables, used_send_at, and delivery_path (single | bulk_native | bulk_expanded) alongside the #86 fields; "email batch sent" summary for sendBatch; captureException for reportable send errors. - telemetrySource moved off the public EmailClientOptions type onto an internal module seam (setTelemetrySource) that only the bundled CLI flips; the public surface gains only `telemetry?: boolean`. - CLI reports "cli command run" and flushes captures before exit. - README telemetry disclosure sections and a combined minor changeset. Generated-By: PostHog Code Task-Id: ec2538f2-5c80-4142-b4b3-b1a53394862e * feat(docs): PostHog web analytics and error monitoring on email-sdk.dev Port of the docs-site half of #86: posthog-js initialized once on hydration in __root.tsx (2026-01-30 defaults, capture_exceptions, web vitals, identified_only person profiles, session recording disabled), privacy page disclosure updates, and CSP pinned to the exact PostHog hosts (us-assets.i.posthog.com in script-src; us.i.posthog.com and us-assets.i.posthog.com in connect-src — no wildcards). Generated-By: PostHog Code Task-Id: ec2538f2-5c80-4142-b4b3-b1a53394862e * ci(release): annotate published versions in PostHog Posts a project-scoped annotation to PostHog project 468042 when changesets publishes, so release markers line up with telemetry charts. Skips gracefully when POSTHOG_PERSONAL_API_KEY is absent and never fails the release job. Generated-By: PostHog Code Task-Id: ec2538f2-5c80-4142-b4b3-b1a53394862e * fix(telemetry): redact error names, harden test isolation, close redaction gaps Adversarial-review fixes: - error.name now runs through the redaction pipeline (allowlisted SDK/builtin names pass verbatim) before it reaches $exception_list type, error_name, the class dedupe key, or $exception_fingerprint - bun test preload forces EMAIL_SDK_TELEMETRY=0 so the in-process suite can never capture live or write telemetry state, regardless of NODE_ENV - home-dir redaction now consumes the remaining path segments on both separators (~<path-redacted>) instead of leaking filenames after the collapse - key=value secrets (password/passwd/pwd/pass/secret/token/api_key) redact before the token-length pattern so short values are caught too Generated-By: PostHog Code Task-Id: ec2538f2-5c80-4142-b4b3-b1a53394862e * fix(telemetry): address review nits — explicit console-error opt-out, narrow batchAdapter type, add resetTelemetrySource seam Generated-By: PostHog Code Task-Id: ec2538f2-5c80-4142-b4b3-b1a53394862e
Builds on #80 (stacked PR — merging this lands the changes into the #80 branch; merge #80 afterward to ship everything to
main).What's new
1. Error tracking (
feat(telemetry))captureException()in the telemetry module sends PostHog$exceptionevents through the same zero-dependency transport.$exception_fingerprint = ErrorName:error_codeforEmailSdkErrors.send()calls (handled; usage errors like validation/unknown-adapter excluded) and unexpected CLI crashes (unhandled).source(sdk|cli) on all events,email batch sentsummary event,ci_vendordetection (GitHub Actions, GitLab, CircleCI, Jenkins, Travis, Buildkite, Vercel, generic).2. Docs site analytics (
feat(docs))posthog-json email-sdk.dev: SPA pageviews (history-change), exception autocapture, web vitals. Vercel Analytics stays. Session replay is off by default (one-line flip inapps/fumadocs/src/lib/posthog.ts).vercel.jsonallowshttps://*.posthog.com(script-src + connect-src).3. Release annotations (
ci(release))POSTHOG_PERSONAL_API_KEYrepo secret (scope:annotation:write, project 468042); skips gracefully without it.PostHog workspace (already live, built via MCP)
Verification
bun run release:cipasses (types, 117 package tests + workspace tests, registry checks, full build, pack dry-runs).client created, failedemail sent,$exception, 3×cli command run), and Error Tracking created the issue EmailProviderError — "Resend failed with 401: API key is invalid" with fully resolved, sanitized frames (cli.js,core.js,resend.js— no user paths).Created with PostHog Code