Skip to content

Polyfill Array.prototype.at so web-vitals' CLS tracker stops crashing in Chrome 85–91 - #305

Merged
SpikeyCoder merged 9 commits into
mainfrom
claude/compassionate-newton-5ut0ts
Oct 17, 2026
Merged

SpikeyCoder merged 9 commits into
mainfrom
claude/compassionate-newton-5ut0ts

Conversation

@SpikeyCoder

@SpikeyCoder SpikeyCoder commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Fixes the crash reported on Trello card XzwkYl7L: TypeError: this.o.at is not a function on /.

Cause

The minified stack (index-BshaS873.js:2:3839; our local build has the same hash) points to web-vitals' LayoutShiftManager, which calls this._sessionEntries.at(-1) in its PerformanceObserver callback. Array.prototype.at only exists in Chrome 92+, Safari 15.4+ and Firefox 90+. So in older engines every layout shift threw, and our crash monitoring reported that uncaught error.

The reported "Chrome 79" can't actually run the bundle, which uses ??= (Chrome 85+). The affected engines are Chrome 85–91, or a browser that spoofs its user agent.

Fix

  • src/lib/arrayAt.ts: a small polyfill that matches the spec. It isn't enumerable, has the native property flags, and uses ToNumber on the index (so it throws on BigInt, as native does). It installs itself on import.
  • src/main.tsx: imports it first.

A polyfill keeps CLS from these older, slower devices, which skipping the metric would have lost, so they still count towards each page's p75.

web-vitals' INP tracker also calls .at, but onINP needs interactionId (Chrome 96+), so in practice only CLS was affected. Nothing else in the bundle uses newer built-ins (Object.hasOwn, findLast, structuredClone, …).

Testing

  • Unit tests: npm test passes (74 tests). The new tests/array-at.test.mjs checks:
    • the polyfill matches native at() across indexes, including holes, NaN, ±Infinity, fractions, strings and BigInt;
    • the native property flags;
    • that importing the module installs it.
  • Other checks: npm run lint, tsc -b and vite build pass.
  • Headless Chromium: with Array.prototype.at deleted, the local build served as fundermatch.org so monitoring is on, and three layout shifts set off:
    • On main: this.o.at is not a function three times, and no CLS sent.
    • On this branch: no errors, and both CLS and LCP reported.

I ran several local code-review rounds and applied their findings. Points considered and declined as having no real caller:

  • ToLength for odd array-likes, and Symbol.unscopables;
  • String/TypedArray .at;
  • build-level polyfilling (@vitejs/plugin-legacy / core-js), which would change the build setup and the supported-browser policy beyond this fix.

🤖 Generated with Claude Code

https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5

web-vitals' INP and CLS trackers call Array.prototype.at in their
PerformanceObserver callbacks. In engines without it (Chrome < 92,
Safari < 15.4) every layout shift threw "this.o.at is not a function",
an uncaught error our own monitoring then reported as a crash
(Trello XzwkYl7L, a Chrome 79 visitor on /). LCP is unaffected and is
still collected there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
Review feedback: skipping INP and CLS where .at is missing drops vitals
from the older, slower devices most likely to push a page's p75 into
"poor". A spec-matching, non-enumerable polyfill, installed before
web-vitals starts, fixes the crash and keeps that data. (The reported
"Chrome 79" can't run the bundle, which uses optional chaining: the
affected engines are Chrome 80-91, Safari < 15.4, Firefox < 90, or the
user agent was spoofed.)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
Review round 2: install the polyfill when arrayAt.ts is evaluated and
import it first in monitoringSetup.ts, so it's in place before any later
import's module body runs, not only before web-vitals' callbacks. Use
unary + (ToNumber), which throws on a BigInt as the native at() does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
Review round 3: INP needs interactionId (Chrome 96+), which every engine
with it pairs with a native .at, so the polyfill restores CLS only; and
the bundle's logical assignment needs Chrome 85+.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
Review round 5: production relies on the install-on-import call, which
every test bypassed by calling installArrayAt() itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
Review round 6: a language polyfill belongs at the app entry, not inside
monitoring setup; and check the installed descriptor matches native
rather than only that for…in skips it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015VQKSm8kLY3Q2qRtXGY1X5
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 17, 2026 •

Copy link
Copy Markdown

Deploying funder-finder with  Cloudflare Pages  Cloudflare Pages

Latest commit: f0060d6
Status: ✅  Deploy successful!
Preview URL: https://ddf77125.funder-finder.pages.dev
Branch Preview URL: https://claude-compassionate-newton.funder-finder.pages.dev

View logs

@SpikeyCoder SpikeyCoder changed the title Skip INP/CLS tracking where Array.prototype.at is missing Polyfill Array.prototype.at so web-vitals' CLS tracker stops crashing in Chrome 85–91 Oct 17, 2026
@SpikeyCoder
SpikeyCoder merged commit 57fb1e2 into main Oct 17, 2026
3 checks passed
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.

2 participants