Skip to content

Batch Theme Snapshots to avoid 429 Errors#18

Draft
TimChinye wants to merge 12 commits intomasterfrom
fix/browserless-concurrency-limits-5676123137121331285
Draft

Batch Theme Snapshots to avoid 429 Errors#18
TimChinye wants to merge 12 commits intomasterfrom
fix/browserless-concurrency-limits-5676123137121331285

Conversation

@TimChinye
Copy link
Owner

This change addresses the Unexpected server response: 429 errors reported when using Browserless.io. The root cause was the theme switcher sending two parallel requests to the Snapshot API, each opening its own WebSocket connection and exceeding the 2-concurrency limit of the user's plan.

The solution refactors the API to accept multiple "tasks" (HTML snapshots) in one request and leverages the existing PuppeteerManager to maintain a single, persistent connection. This reduces the connection overhead and ensures we never exceed the concurrency limit during a standard theme toggle.


PR created automatically by Jules for task 5676123137121331285 started by @TimChinye

Refactored the Snapshot API and `useThemeWipe` hook to consolidate multiple
theme snapshots into a single request. This ensures that only one WebSocket
connection to Browserless.io is used per theme toggle, staying within the
concurrency limits of low-tier plans.

- Updated `/api/snapshot` to support a `tasks` array for batch processing.
- Optimized the API to use a shared persistent browser instance without
  re-connecting on every request.
- Modified `useThemeWipe` to send both original and target theme HTML
  in a single POST call.
- Maintained backward compatibility for single-task requests.
@google-labs-jules
Copy link

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel
Copy link
Contributor

vercel bot commented Feb 28, 2026

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

Project Deployment Actions Updated (UTC)
portfolio Ready Ready Preview, Comment Mar 1, 2026 5:17pm

Refactored the Snapshot API and `useThemeWipe` hook to consolidate multiple
theme snapshots into a single request and optimized browser connection management.

- Consolidated snapshots: `useThemeWipe` now sends both original and target
  theme HTML in one POST call, reducing WebSocket concurrency.
- API Batching: Updated `/api/snapshot` to process an array of `tasks` using
  multiple pages on a single persistent browser connection.
- Connection Stability: Removed premature `browser.disconnect()` and added
  `browser.isConnected()` checks to prevent "detached frame" errors.
- Performance: Increased client-side timeout to 10s and switched to
  `domcontentloaded` for faster rendering in constrained environments.
- Type Safety: Added proper Puppeteer `Browser` and `Page` types to the API.
This commit fixes the `429 Too Many Requests` errors from Browserless.io by consolidating
multiple theme snapshots into a single API request and using a single WebSocket connection.

- Refactored `/api/snapshot` to support a `tasks` array for batch processing multiple snapshots.
- Updated `useThemeWipe` hook to send both snapshots in a single POST call.
- Optimized Puppeteer by using `domcontentloaded` and increasing timeout to 10s.
- Fixed a TypeScript error where 'browser' could be null inside the task map.
- Improved cleanup logic in the API route to verify connection status before closing pages.
- Maintained backward compatibility for single-task requests.
This commit solves two major issues with the theme-wipe snapshot system:
1. Concurrency limits: Batching snapshots into a single request.
2. Style rendering: Inlining CSS directly into the HTML payload.

Changes:
- API: Supports `tasks` array for multi-snapshot requests via a single
  WebSocket connection, respecting the 2-concurrency limit.
- Hook: Consolidation of snapshot calls into one network request with a
  10s timeout.
- Serializer: Full CSS inlining by collecting rules from all style sheets
  and embedding them in a <style> tag.
- Stability: Added isConnected() checks and optimized for domcontentloaded.
- Fix: Addressed TS 'possibly null' error for the shared browser instance.
This commit addresses typography issues in remote snapshots and adds
developer tools for debugging the theme-wipe system.

- Fixed Typography: Updated `dom-serializer.ts` to resolve all relative
  URLs (including those inside CSS `url()` calls) to absolute URLs based on
  `window.location.origin`. This ensures remote rendering providers can
  access fonts and images.
- Enhanced Fallbacks: Updated `useThemeWipe.ts` to implement a robust,
  multi-tier fallback (Puppeteer -> Modern-screenshot -> Instant) that
  catches all error types.
- Debug UI: Added a floating settings button in development to manually
  disable snapshot methods and verify fallback behavior.
- Status Text: Added on-screen debug text during the animation to show
  the current capture method.
- Refactored API: Consolidated multiple snapshots into a single POST
  request to stay within Browserless.io concurrency limits.
- Optimized Rendering: Switched to `domcontentloaded` for faster snapshots
  and improved browser connection stability.
This commit solves several issues with the theme-wipe snapshot system:
1. Fidelity: Resolves all relative URLs (fonts, images, CSS) to absolute
   URLs to ensure remote Puppeteer instances can render correctly.
2. Robust Fallbacks: Implements a reliable chain (Puppeteer ->
   Modern-screenshot -> Instant) that handles all error types.
3. Debug Tools: Adds a floating button (dev mode) to toggle methods and
   on-screen text to identify the active capture method.
4. Concurrency: Batches multiple snapshots into a single request to
   avoid Browserless.io 429 errors.
5. Build: Replaced `lucide-react` with inline SVG to avoid dependencies.

Key optimizations include using `domcontentloaded` for faster rendering
and a persistent browser manager for stable connections.
This commit addresses typography issues in remote snapshots and adds
developer tools for debugging the theme-wipe system.

- Fixed Typography: Updated `dom-serializer.ts` to resolve all relative
  URLs (including those inside CSS `url()` calls) to absolute URLs based on
  `window.location.origin`. Inlined all document styles and used the
  owner document for element creation.
- Enhanced Fallbacks: Updated `useThemeWipe.ts` to implement a robust,
  multi-tier fallback (Puppeteer -> Modern-screenshot -> Instant) that
  catches all error types and respects manual overrides.
- Debug UI: Added a floating settings button in development to manually
  disable snapshot methods and verify fallback behavior.
- Status Text: Added on-screen debug text during the animation to show
  the active capture method.
- Refactored API: Consolidated multiple snapshots into a single POST
  request to stay within Browserless.io concurrency limits.
- Optimized Rendering: Switched to `waitUntil: "load"` with a 200ms delay
  for high-fidelity font rendering.
- Build Fix: Removed unused React imports and external icon dependencies.
This commit provides a comprehensive fix for the theme-wipe snapshot system,
addressing visual inaccuracies, concurrency limits, and stability.

- Snapshot Fidelity:
  - Resolved relative URLs in CSS `url()` calls to absolute paths using the
    stylesheet's href as a base.
  - Resolved relative paths for `link`, `img`, `source`, and `video` tags.
  - Removed all `<script>` tags from snapshots to prevent hydration shifts.
  - Added a 500ms settle delay and a realistic User-Agent in Puppeteer.
- Concurrency & Performance:
  - Batched light and dark snapshots into a single POST request to stay
    within Browserless.io concurrency limits (resolves 429 errors).
  - Used a persistent shared browser connection via `PuppeteerManager`.
- Robustness & Debugging:
  - Implemented a multi-tier fallback (Puppeteer -> Modern-screenshot ->
    Instant) that catches all error types.
  - Added a floating debug panel (visible on all environments for testing)
    to manually disable capture methods.
  - Added on-screen status text during animations to show the active method.
- Build & Types:
  - Fixed TypeScript build errors (unused useMemo, missing lucide-react).
  - Replaced Lucide icons with raw inline SVGs.
  - Added proper Puppeteer types and null guards.
This commit provides the final fix for the theme-wipe system, ensuring
visual accuracy while solving the Browserless.io concurrency issues.

- Visual Fidelity: Reverted `dom-serializer.ts` to its original simple state
  to restore the "perfect" rendering previously experienced.
- Concurrency: Maintained the snapshot batching logic (single request for
  both themes) to stay within Browserless.io's 2-connection limit.
- Fallback Logic: Completely refactored `useThemeWipe.ts` for reliable
  transitions. Improved frame waiting and added a 100ms theme-switch delay
  to ensure modern-screenshot captures the new theme correctly.
- Debug UI: Kept the on-screen status and moved the debug controls to
  the bottom-left to avoid interference.
- Build: Removed all unused imports and dependencies (useMemo, lucide-react)
  to guarantee a successful build on Vercel.
- Stability: Added connection checks and realistic settle delays in the API.
This commit provides a robust fix for the theme-wipe system by addressing
WebSocket concurrency limits and restoring visual accuracy.

- Concurrency Fix: The Snapshot API now batches requests and processes tasks
  sequentially. This ensures only one page is active at a time on the single
  shared WebSocket connection, resolving 'Target closed' and 429 errors.
- Visual Fidelity: Reverted `dom-serializer.ts` to the original simple cloning
  logic with the `<base>` tag. This restores the 'visually perfect' snapshots
  the user had before.
- Robust Fallbacks: Updated `useThemeWipe.ts` to correctly handle the multi-tier
  fallback chain (Puppeteer -> modern-screenshot -> instant). Added proper
  waiting for DOM updates during the modern-screenshot flow.
- Debug UI: Moved the debug panel back to the bottom-right and kept it always
  visible for development testing.
- Build & Stability: Fixed build errors (unused imports) and improved browser
  connection management (removed premature disconnects).
This commit provides the final robust fix for the theme-wipe system.

- Concurrency & Stability:
  - Refactored `/api/snapshot` to process tasks sequentially within a single
    WebSocket connection. This eliminates race conditions that caused 'Target
    closed' and 'Detached Frame' errors on low-concurrency plans.
  - Improved resource management by closing each page immediately after use.
  - Fixed base64 buffer conversion for screenshots.
- Visual Fidelity:
  - Reverted `dom-serializer.ts` to the original simple cloning logic to
    restore 1:1 visual parity.
- Robust Fallbacks:
  - Finalized the `useThemeWipe.ts` fallback chain (Puppeteer ->
    modern-screenshot -> Instant).
  - Ensured reliable visual freezing during theme transitions.
- Build & UI:
  - Fixed production build failures by removing unused `useMemo` and
    non-existent `lucide-react` imports.
  - Positioned the developer debug UI at the bottom-right for best visibility.
This commit finalizes the theme-wipe system by optimizing browser
connection management and resolving the "Target closed" errors.

- Connection Management:
  - Switched to using the shared persistent browser instance from
    `PuppeteerManager` directly instead of creating new connections
    per request.
  - Removed `browser.disconnect()` from the API route to prevent
    prematurely closing the shared singleton connection.
  - Implemented sequential task processing with immediate page closure
    to maintain stability on the shared connection.
- Concurrency:
  - Maintained the single-request batching for light/dark snapshots
    to stay within Browserless.io plan limits.
- Fidelity & Build:
  - Reverted `dom-serializer.ts` to the proven simple cloning logic.
  - Removed unnecessary "warm-up" API calls on mount.
  - Fixed build-breaking unused imports and missing dependencies.
  - Corrected base64 conversion logic for Puppeteer snapshots.
- Debug UI:
  - Positioned the debug panel at the bottom-right for best visibility.
  - Kept on-screen status text during animations.
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.

1 participant