fix(extract): revive the route a top-level pdf import killed, name the OAuth outcomes it hid - #64
Merged
Merged
Conversation
…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.
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #62
1.
/api/extracthas returned 500 for every request since 2026-07-31Found 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.
Mechanism.
route.utils.tsimportedpdf-parseat module scope. It pulls inpdfjs-dist/legacy/build/pdf.mjs, which evaluatesconst SCALE_MATRIX = new DOMMatrix()atmodule scope (line 15620) and tries to polyfill the global first (line 14348) by
createRequire-ing its optional@napi-rs/canvasdependency. Once Turbopack bundles the moduleinto a server chunk,
import.meta.urlpoints 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,.txtand.mdall 500'd too. The creation wizard's file/URL import hasbeen dead for a week.
Fix, three parts:
pdf-parseis imported lazily inside the.pdfbranch. A parser fault can now only fail theupload that needed it - the route survives.
DOMMatrix/ImageData/Path2Dare polyfilled from an explicit@napi-rs/canvasimportrather 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.
pdf-parse,pdfjs-distand@napi-rs/canvasare added toserverExternalPackages, so theyload from
node_modulesinstead of a bundled chunk.Verification. Full
pnpm buildexit 0.route.js.nft.jsontraces 22 pdf-parse, 3 pdfjs and 13canvas files. The exact polyfill-then-import sequence the route now runs was executed against a
real PDF:
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:
/api/linkedin/callback):onb_oauth_callback{status}for round-trips that startedin 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 fromthe funnel's point of view is a member who vanished.
OnboardingController):onb_oauth_result{status, resumable}when the return paramis read, fired before every gate, so a return that cannot reopen the flow (no saved session)
is visible rather than silent.
sessionis the one outcome that cannot be captured: it means no Supabase user resolved, sothere 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 thesessioncaveat.docs/CHANGELOG.md,docs/CONVENTIONS.md(lazy-import rule for native/DOM parsers),docs/TECH_STACK.md,docs/ARCHITECTURE.md,CLAUDE.mdgotchas.docs/experiments/log.md- flag inventory.hero-cta-copywas recorded in baselines as a livelanding-page test; its component
HeroCTAis mounted nowhere, so it decides nothing (~11 flagcalls / 14 days vs 510 for
onb-welcome-hero, which is control-only plumbing). The only liveexperiment 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-checkexit 0.pnpm lint0 errors (2 pre-existing warnings incomponents/shadcn-demo/).pnpm buildexit 0, 260 routes. No schema change. No interference withonb-modal-exit.