|
| 1 | +# Prerendering Staleness Detection |
| 2 | + |
| 3 | +This document describes the implementation of staleness detection for prerendered checkout pages. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +When using prerendering with Speculation Rules, checkout pages may become stale if the cart state changes after prerendering but before the user navigates to the checkout. This implementation automatically detects and refreshes stale checkout data without causing jank for users. |
| 8 | + |
| 9 | +## Implementation |
| 10 | + |
| 11 | +### Core Components |
| 12 | + |
| 13 | +1. **PrerenderingStalenessDetector**: Captures and compares checkout snapshots |
| 14 | +2. **Prerendering Change Handler**: Handles the `prerenderingchange` event |
| 15 | +3. **Global Refresh API**: Exposed on `window.checkoutRefreshAPI` for external use |
| 16 | + |
| 17 | +### Detection Strategy |
| 18 | + |
| 19 | +The system compares these key identifiers to detect staleness: |
| 20 | + |
| 21 | +- Cart ID |
| 22 | +- Cart updated time |
| 23 | +- Cart item count (physical + digital + gift certificates) |
| 24 | +- Cart base amount |
| 25 | +- Checkout ID |
| 26 | +- Number of consignments |
| 27 | + |
| 28 | +### Usage |
| 29 | + |
| 30 | +#### Automatic Detection (Built-in) |
| 31 | + |
| 32 | +The implementation automatically: |
| 33 | + |
| 34 | +1. Captures initial snapshot when a page is prerendered |
| 35 | +2. Listens for the `prerenderingchange` event |
| 36 | +3. Refreshes checkout data in the background if changes are detected |
| 37 | +4. Logs when refresh occurs due to staleness |
| 38 | + |
| 39 | +#### Manual Refresh API |
| 40 | + |
| 41 | +The global API is available at `window.checkoutRefreshAPI`: |
| 42 | + |
| 43 | +```typescript |
| 44 | +// Check if checkout data is stale |
| 45 | +const isStale = window.checkoutRefreshAPI?.isCheckoutStale(); |
| 46 | + |
| 47 | +// Force refresh checkout data |
| 48 | +const result = await window.checkoutRefreshAPI?.refreshCheckout(true); |
| 49 | +console.log('Refresh success:', result.success, 'Was stale:', result.wasStale); |
| 50 | + |
| 51 | +// Get current snapshot for debugging |
| 52 | +const snapshot = window.checkoutRefreshAPI?.getCurrentSnapshot(); |
| 53 | +console.log('Current checkout state:', snapshot); |
| 54 | +``` |
| 55 | + |
| 56 | +#### External Integration Examples |
| 57 | + |
| 58 | +```javascript |
| 59 | +// Example: Refresh when cart is modified in another tab |
| 60 | +window.addEventListener('storage', async (e) => { |
| 61 | + if (e.key === 'cart_modified') { |
| 62 | + const result = await window.checkoutRefreshAPI?.refreshCheckout(); |
| 63 | + if (result?.wasStale) { |
| 64 | + console.log('Checkout was refreshed due to cart changes'); |
| 65 | + } |
| 66 | + } |
| 67 | +}); |
| 68 | + |
| 69 | +// Example: Periodic staleness check |
| 70 | +setInterval(async () => { |
| 71 | + if (window.checkoutRefreshAPI?.isCheckoutStale()) { |
| 72 | + await window.checkoutRefreshAPI.refreshCheckout(); |
| 73 | + } |
| 74 | +}, 30000); // Check every 30 seconds |
| 75 | +``` |
| 76 | +
|
| 77 | +## Files Modified |
| 78 | +
|
| 79 | +- `packages/core/src/app/checkout/prerenderingStalenessDetector.ts` - Core implementation |
| 80 | +- `packages/core/src/app/checkout/CheckoutPage.tsx` - Integration with checkout page |
| 81 | +- `packages/core/types/dom.extended.d.ts` - Type definitions for global API |
| 82 | +
|
| 83 | +## Benefits |
| 84 | +
|
| 85 | +1. **Seamless UX**: No jank when checkout data hasn't changed |
| 86 | +2. **Automatic Updates**: Stale data is refreshed transparently |
| 87 | +3. **External API**: Allows custom refresh triggers from external code |
| 88 | +4. **Debugging Support**: Comprehensive logging and inspection capabilities |
| 89 | +
|
| 90 | +## Edge Cases Handled |
| 91 | +
|
| 92 | +- Missing cart/checkout data |
| 93 | +- Network failures during refresh (graceful degradation) |
| 94 | +- Multiple rapid refresh attempts |
| 95 | +- Component unmounting during refresh |
| 96 | +- API cleanup on page navigation |
| 97 | +
|
| 98 | +## Testing |
| 99 | +
|
| 100 | +Comprehensive test coverage includes: |
| 101 | +- Staleness detection accuracy |
| 102 | +- Error handling |
| 103 | +- API functionality |
| 104 | +- Integration with existing prerendering flow |
| 105 | +
|
| 106 | +Run tests with: |
| 107 | +```bash |
| 108 | +npx jest packages/core/src/app/checkout/prerenderingStalenessDetector.test.ts --config=packages/core/jest.config.js |
| 109 | +``` |
0 commit comments