Skip to content

fix(extract): revive the route a top-level pdf import killed, name the OAuth outcomes it hid - #64

Merged
gatteo merged 1 commit into
mainfrom
fix/extract-pdf-crash-and-oauth-events
Aug 6, 2026
Merged

fix(extract): revive the route a top-level pdf import killed, name the OAuth outcomes it hid#64
gatteo merged 1 commit into
mainfrom
fix/extract-pdf-crash-and-oauth-events

Conversation

@gatteo

@gatteo gatteo commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Closes #62

1. /api/extract has returned 500 for every request since 2026-07-31

Found in the Vercel runtime errors, not in the funnel data - nothing in the onboarding flow
touches this route, so eight consecutive GREEN health runs never looked at it.

ReferenceError: DOMMatrix is not defined
  at module evaluation (.next/server/chunks/[root-of-the-server]__3f3b0067._.js:6:117578)
  at Object.<anonymous> (.next/server/app/api/extract/route.js:10:3)
  page: '/api/extract'

Mechanism. route.utils.ts imported pdf-parse at module scope. It pulls in
pdfjs-dist/legacy/build/pdf.mjs, which evaluates const SCALE_MATRIX = new DOMMatrix() at
module scope (line 15620) and tries to polyfill the global first (line 14348) by
createRequire-ing its optional @napi-rs/canvas dependency. Once Turbopack bundles the module
into a server chunk, import.meta.url points inside .next/server/chunks/, the require misses,
the polyfill is skipped, and the bare new DOMMatrix() throws.

Because that happens while the route module is evaluating, the failure is not scoped to PDFs:
URL extraction, .docx, .txt and .md all 500'd too. The creation wizard's file/URL import has
been dead for a week.

Fix, three parts:

  1. pdf-parse is imported lazily inside the .pdf branch. A parser fault can now only fail the
    upload that needed it - the route survives.
  2. DOMMatrix/ImageData/Path2D are polyfilled from an explicit @napi-rs/canvas import
    rather than left to pdfjs's runtime lookup. The static specifier is what makes the package
    visible to Next's file tracer: the route traced 0 canvas files before this change and 13
    after, so the native binary now actually reaches the deployment.
  3. pdf-parse, pdfjs-dist and @napi-rs/canvas are added to serverExternalPackages, so they
    load from node_modules instead of a bundled chunk.

Verification. Full pnpm build exit 0. route.js.nft.json traces 22 pdf-parse, 3 pdfjs and 13
canvas files. The exact polyfill-then-import sequence the route now runs was executed against a
real PDF:

polyfilled DOMMatrix: function
EXTRACTED: "LinkedIn preview extraction smoke test\nSecond line of content.\n\n-- 1 of 1 --"

2. OAuth outcomes are now observable (#62)

OAuth is the only lossy connect method - 21/29 progressed past connect this window against 15/15
for URL and 17/17 for skip - and every failure fired no event, so a declined consent screen, a
broken token exchange and a closed tab were indistinguishable. Instrumentation only, no behaviour
change:

  • Server (/api/linkedin/callback): onb_oauth_callback{status} for round-trips that started
    in onboarding. The Supabase user is resolved once up front so early exits (denied, error,
    unavailable) can be attributed. The account-switch exits (linked-elsewhere, merge-prompt,
    signin-failed, welcome) report too - they end the round-trip on the settings page, which from
    the funnel's point of view is a member who vanished.
  • Client (OnboardingController): onb_oauth_result{status, resumable} when the return param
    is read, fired before every gate, so a return that cannot reopen the flow (no saved session)
    is visible rather than silent.
  • session is the one outcome that cannot be captured: it means no Supabase user resolved, so
    there is no distinctId. Documented in the dictionary rather than faked.

The pair separates "never came back from LinkedIn" (no server event) from "came back but the flow
did not reopen" (server event, no client event) - the split the last three audits could not make.

3. Docs

  • docs/analytics/onboarding-funnel.md - both new events, including the session caveat.
  • docs/CHANGELOG.md, docs/CONVENTIONS.md (lazy-import rule for native/DOM parsers),
    docs/TECH_STACK.md, docs/ARCHITECTURE.md, CLAUDE.md gotchas.
  • docs/experiments/log.md - flag inventory. hero-cta-copy was recorded in baselines as a live
    landing-page test; its component HeroCTA is mounted nowhere, so it decides nothing (~11 flag
    calls / 14 days vs 510 for onb-welcome-hero, which is control-only plumbing). The only live
    experiment is onb-modal-exit. Also records the queued connect experiment ([funnel] experiment: answers-first at connect, OAuth demoted to secondary (onb-connect-answers-first) #63).

Checks

pnpm type-check exit 0. pnpm lint 0 errors (2 pre-existing warnings in components/shadcn-demo/).
pnpm build exit 0, 260 routes. No schema change. No interference with onb-modal-exit.

…uth outcomes

/api/extract returned 500 for every request since 2026-07-31. pdf-parse was
imported at module scope; pdfjs evaluates `new DOMMatrix()` there and only
self-polyfills through a runtime createRequire of its optional @napi-rs/canvas
dep, which fails once the module is bundled into a server chunk. The route died
while evaluating, so URL, .docx, .txt and .md extraction went down with the PDF
path that caused it.

- import pdf-parse lazily inside the PDF branch so a parser fault can never take
  the whole route with it again
- polyfill DOMMatrix/ImageData/Path2D from an explicit @napi-rs/canvas import:
  a static specifier the file tracer can follow (0 canvas files traced before,
  13 after)
- add pdf-parse, pdfjs-dist and @napi-rs/canvas to serverExternalPackages

Also closes the OAuth observability gap (#62): the callback fires
onb_oauth_callback{status} for onboarding-origin round-trips including the
account-switch exits, and the controller fires onb_oauth_result{status,
resumable} before every gate, so a return that cannot reopen the flow stops
looking like a member who never came back.
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
linkedinpreview-com Building Building Preview Aug 6, 2026 9:47am

Request Review

@gatteo
gatteo merged commit 0729d9e into main Aug 6, 2026
2 of 3 checks passed
@gatteo
gatteo deleted the fix/extract-pdf-crash-and-oauth-events branch August 6, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[funnel] connect: OAuth is the only lossy method and its failures fire no event (-9 users/week unexplained)

1 participant