Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions apps/wodsmith-start/src/lib/posthog/client.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import posthog from "posthog-js"

import { filterPostHogEvent } from "./event-filter"

const POSTHOG_KEY =
import.meta.env.VITE_POSTHOG_KEY ||
"phc_UCtCVOUXvpuKzF50prCLKIWWCFc61j5CPTbt99OrKsK"
Expand Down Expand Up @@ -41,6 +43,7 @@ export function initPostHog(): void {
ui_host: "https://us.posthog.com",
// Match Next.js app settings
defaults: "2025-05-24",
before_send: filterPostHogEvent,
capture_exceptions: true,
debug: false,
// We'll handle pageviews manually with router integration
Expand Down
27 changes: 27 additions & 0 deletions apps/wodsmith-start/src/lib/posthog/event-filter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { CaptureResult } from "posthog-js"

const INSTAGRAM_WEBVIEW_BRIDGE = "window.webkit.messageHandlers"

/**
* Drop errors from Instagram's injected iOS webview bridge while preserving
* application exceptions and every other PostHog event.
*/
// @lat: [[architecture#Client Error Filtering]]
export function filterPostHogEvent(
event: CaptureResult | null,
): CaptureResult | null {
if (event?.event !== "$exception") {
return event
}

const exceptionList = event.properties.$exception_list
const isInstagramBridgeError =
Array.isArray(exceptionList) &&
exceptionList.some(
(exception) =>
typeof exception?.value === "string" &&
exception.value.includes(INSTAGRAM_WEBVIEW_BRIDGE),
)

return isInstagramBridgeError ? null : event
}
40 changes: 40 additions & 0 deletions apps/wodsmith-start/test/lib/posthog-event-filter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import type { CaptureResult } from "posthog-js"
import { describe, expect, it } from "vitest"

import { filterPostHogEvent } from "@/lib/posthog/event-filter"

function exceptionEvent(value: string): CaptureResult {
return {
uuid: "exception-event-id",
event: "$exception",
properties: {
$exception_list: [{ type: "TypeError", value }],
},
}
}

describe("filterPostHogEvent", () => {
it("drops the broken Instagram iOS webview bridge exception", () => {
const event = exceptionEvent(
"undefined is not an object (evaluating 'window.webkit.messageHandlers')",
)

expect(filterPostHogEvent(event)).toBeNull()
})

it("preserves unrelated exceptions", () => {
const event = exceptionEvent("Something in the application failed")

expect(filterPostHogEvent(event)).toBe(event)
})

it("preserves non-exception events", () => {
const event: CaptureResult = {
uuid: "pageview-event-id",
event: "$pageview",
properties: {},
}

expect(filterPostHogEvent(event)).toBe(event)
})
})
6 changes: 6 additions & 0 deletions lat.md/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ TanStack Start (React + Vite) deployed to Cloudflare Workers, with PlanetScale (
- **Deployment**: Alchemy IaC to Cloudflare Workers, Vite build pipeline
- **Testing**: Vitest (unit/integration), Playwright (E2E), Testing Library (components)

## Client Error Filtering

Client error capture excludes known browser-injected failures while preserving application exceptions and analytics events.

[[apps/wodsmith-start/src/lib/posthog/event-filter.ts#filterPostHogEvent]] drops only PostHog `$exception` events containing `window.webkit.messageHandlers`. Instagram's iOS in-app browser injects this broken native bridge; the string does not occur in WODsmith application source. Other exceptions and non-exception events pass through unchanged.

## Monorepo Structure

The monorepo uses Turborepo with pnpm workspaces. The main app lives in `apps/wodsmith-start/`.
Expand Down
Loading