fix(app): polyfill requestIdleCallback for iOS Safari#5816
Merged
Conversation
Safari/iOS doesn't ship requestIdleCallback by default (caniuse / WebKit bug 164193 — only available behind the experimental-features flag). Calling window.requestIdleCallback on the iPhone therefore threw TypeError at mount of AppDataProvider, which the ErrorBoundary caught — leaving every iOS user stuck on the error fallback after PR #5808's debug rollout surfaced it. Inline feature-detect in the existing useEffect: use requestIdleCallback when present, else fall back to setTimeout(cb, 1). Cleanup picks the matching cancel function via captured boolean. No new dependency, no global mutation. Adds a regression test that stubs both idle APIs as undefined and asserts the three initial fetches (/specs, /libraries, /stats) still fire and children still render — locking in the fix for future refactors. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
There was a problem hiding this comment.
Pull request overview
Fixes a hard crash on iOS Safari/WebKit where window.requestIdleCallback is undefined, preventing the app from loading by ensuring AppDataProvider schedules its initial data fetches via a safe fallback.
Changes:
- Feature-detect
window.requestIdleCallbackand fall back tosetTimeoutwhen unavailable; clean up with the matching cancel API. - Add a regression test that simulates iOS Safari by stubbing the idle APIs as unavailable and asserting initial fetches still occur and children render.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| app/src/components/Layout.tsx | Adds requestIdleCallback feature-detect with setTimeout fallback for initial shared-data loading. |
| app/src/components/Layout.test.tsx | Adds a regression test covering the fallback behavior when idle callbacks are missing. |
Comment on lines
+131
to
+135
| it('falls back to setTimeout when requestIdleCallback is unavailable (iOS Safari)', async () => { | ||
| // Simulate Safari/iOS where requestIdleCallback is undefined by default. | ||
| vi.stubGlobal('requestIdleCallback', undefined); | ||
| vi.stubGlobal('cancelIdleCallback', undefined); | ||
|
|
Owner
Author
There was a problem hiding this comment.
Addressed in 585ba50 — added vi.unstubAllGlobals() at the end of the test so subsequent tests do not see leaked undefined globals.
Addresses Copilot review comment on PR #5816: the new test stubbed requestIdleCallback/cancelIdleCallback to undefined and never restored them. The existing beforeEach re-stubs at the start of the next test, but a future test inserted between this one and another that depends on the globals would see undefined and break silently. Adding vi.unstubAllGlobals() at the end of the test makes the leak impossible. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
TypeError: window.requestIdleCallback is not a functionatAppDataProvidermount, whichErrorBoundarycaught — leaving the user stuck on the error fallback. Surfaced by the on-device debug console added in feat(debug): on-device console + richer ErrorBoundary for mobile crashes #5808.requestIdleCallbackis documented as unsupported on Safari/iOS by caniuse, MDN, and WebKit bug 164193. It exists only behind the Experimental Features toggle, which is off by default — so this is a hard regression for every iOS visitor, not a one-off device bug.useEffect: userequestIdleCallbackif present, otherwise fall back tosetTimeout(cb, 1). Cleanup picks the matching cancel function via captured boolean. No new npm dependency, no global mutation.undefinedand asserts the three initial fetches still fire and children still render.Original iPhone error report
Test plan
cd app && yarn test --run— 467/467 pass, including newfalls back to setTimeout when requestIdleCallback is unavailable (iOS Safari)case inLayout.test.tsx.cd app && yarn build— TypeScript / Vite build green.mainunchanged; no new findings introduced by this PR.Why this approach (not a polyfill package)
setTimeout— there is no way to truly polyfill real browser idle time. Adding a dependency would ship bytes to every modern-browser user for the same effect we get inline.utils/idleCallback.ts.🤖 Generated with Claude Code