…K-internal errors out of error.count
B14-2082 (snabbit app-webview, Android 8.1 / Chrome 87). web-vitals 5
calls Array.prototype.at while tracking CLS and INP; Chrome < 92 has no
`at`, so its observer callback threw "this.o.at is not a function" on
every layout shift for the life of the page, and our own error tracker
filed each one as an uncaught application error. The SDK never calls
`at` itself: web-vitals is a runtime dependency the app's bundler
inlines next to us, which is why the stack named a scout-<hash>.js
chunk. CLS and INP are now registered only where `at` exists; LCP, FCP
and TTFB keep reporting.
Errors whose top frame is inside the SDK bundle (or web-vitals) now
carry error.origin=sdk and error.category=sdk_internal, are emitted
once per distinct failure per page, and are excluded from error.count,
view.error.count and error_click detection; everything else carries
error.origin=app. Each instrumentation is mounted in its own guard so a
throwing installer cannot abort initialize() before the providers are
registered. New `make check-compat` (in `make ci`) fails the build when
dist/ calls a runtime API newer than Chrome / Android WebView 87; README
documents that floor.
Summary
Fixes B14-2082 (snabbit
app-webview-rum:TypeError: this.o.at is not a functionfromscout-LfkEtGwo.json Android 8.1 / Chrome 87, counted as an application error)..at()comes from. Not the SDK source (tsconfiglib: ES2020would reject it).web-vitals@5.2.0callsArray.prototype.atin its layout-shift (CLS) and interaction (INP) managers. tsup leavesweb-vitalsexternal, so the customer's Vite build inlines it next to the SDK in thescout-<hash>.jschunk. Chrome < 92 has noat, so the observer callback threw on every layout shift for the page's lifetime. The SDK otherwise kept working; CLS was silently never reported.Array.prototype.atexists; LCP, FCP and TTFB still report. No polyfill is shipped (a global shim from an SDK is a footgun for host apps, and esbuild targets lower syntax, never prototype methods).web-vitals(src/core/sdk-origin.ts) now carrieserror.origin: sdkanderror.category: sdk_internal, is emitted once per distinct fingerprint per page, and is excluded fromerror.count,view.error.countanderror_clickfrustration detection. Every other error carrieserror.origin: app. The plugin can filter onerror.origin.install*()inScout.initializeis mounted in its own guard; a synchronous throw used to leave_providersunset (soflush()/shutdown()became no-ops) and skip every later instrumentation.make check-compat(now inmake ci) grepsdist/for runtime APIs newer than Chrome / Android WebView 87 (.at(,structuredClone,Object.hasOwn,findLast, …). README gains a "Browser support" section.Tests
web-vitals.test.ts: CLS/INP skipped whenArray.prototype.atis deleted, all five registered otherwise.sdk-origin.test.ts: V8 and JavaScriptCore stack shapes, Vite chunk, unbundled files, app error passing through the SDK staysapp.error.test.ts: sdk-origin error flagged and excluded fromerror.count/view.error.count, reported once per page; app errors counted.frustration.test.ts: app error after a click →error_click; SDK error → not.make cigreen locally (build +check-compat+ export checks).Note on ticket wording
The ticket proposed
error.source_type: sdk; that field carries the platform (browser/react-native) and the plugin classifies native crashes on it, so the origin lives in a newerror.originattribute instead.