Documentation Index
Fetch the complete documentation index at: https://docs.uplink.build/llms.txt
Use this file to discover all available pages before exploring further.
Monitor page events including HTTP requests, navigation, DOM changes, and custom events emitted by the page.
Methods
page.on()
Listens for page events. Returns an AbortController for cleanup.
page.on(event: string, handler: EventListener): Promise<AbortController>
Parameters:
event: Event type to listen for
handler: Function to handle the event
Returns: Promise<AbortController> - Controller for stopping the listener
Example:
const controller = await page.on('xhr', async (event) => {
console.log('HTTP Request:', event.data.url)
})
// Later, stop listening
await page.off(controller)
page.once()
Listens for a page event once, then automatically removes the listener.
page.once(event: string, handler: EventListener): Promise<AbortController>
Parameters:
event: Event type to listen for
handler: Function to handle the event
Returns: Promise<AbortController> - Controller (listener auto-removed after first event)
Example:
await page.once('finished', async (event) => {
console.log('Page finished loading')
})
page.off()
Stops listening to events by providing the AbortController.
page.off(controller: AbortController): void
Parameters:
controller: AbortController returned from on() or once()
Example:
const controller = await page.on('domchange', handler)
await page.off(controller)
Available events
xhr
Emitted when an HTTP request (fetch/XHR) is made.
Event data:
{
type: string // e.g., "fetch", "xhr"
url: string // Request URL
method: string // HTTP method
headers: Record<string, string> // Request headers
data: Record<string, unknown> // Request body
response?: {
status: string // Response status code
data: Record<string, unknown> // Response body
headers: Record<string, string> // Response headers
}
}
Example:
await page.on('xhr', async (event) => {
const request = event.data
console.log(`${request.method} ${request.url}`)
if (request.response) {
console.log(`Response status: ${request.response.status}`)
}
})
started
Emitted when page load starts.
Example:
await page.on('started', async (event) => {
console.log('Page load started')
})
finished
Emitted when page load finishes.
Example:
await page.on('finished', async (event) => {
console.log('Page load finished')
})
locationchange
Emitted when the page URL changes.
Example:
await page.on('locationchange', async (event) => {
const newUrl = await page.url()
console.log('URL changed to:', newUrl)
})
domchange
Emitted when the DOM is mutated.
Example:
await page.on('domchange', async (event) => {
console.log('DOM changed')
})
dispatch
Emitted for custom events dispatched by the page.
Example:
await page.on('dispatch', async (event) => {
console.log('Custom event:', event.data)
})
close
Emitted when the browser requests to close (e.g., user clicks close).
Example:
await page.on('close', async (event) => {
console.log('Browser close requested')
// Cleanup or save state
})
closed
Emitted when the page is actually closed.
Example:
await page.on('closed', async (event) => {
console.log('Page closed')
})
hostblocked
Emitted when a host is blocked by an allowlist.
Example:
await page.on('hostblocked', async (event) => {
console.log('Blocked host:', event.data)
})
Complete examples
Monitor all HTTP requests
const controller = await page.on('xhr', async (event) => {
const req = event.data
console.log(`[${req.method}] ${req.url}`)
if (req.response) {
console.log(` Status: ${req.response.status}`)
console.log(` Headers:`, req.response.headers)
}
})
await page.goto('https://example.com')
// Stop monitoring after some time
await new Promise(resolve => setTimeout(resolve, 10000))
await page.off(controller)
Filter API requests
await page.on('xhr', async (event) => {
const req = event.data
// Only log API calls
if (req.url.includes('/api/')) {
console.log('API Request:', req.method, req.url)
if (req.response) {
console.log('API Response:', req.response.status)
// Log response data for specific endpoints
if (req.url.includes('/api/users')) {
console.log('Users data:', req.response.data)
}
}
}
})
await page.goto('https://example.com/app')
Track page lifecycle
await page.on('started', async () => {
console.log('[Lifecycle] Page load started')
})
await page.on('finished', async () => {
console.log('[Lifecycle] Page load finished')
})
await page.on('locationchange', async () => {
const url = await page.url()
console.log('[Lifecycle] URL changed:', url)
})
await page.goto('https://example.com')
await page.click('#navigate-link')
Count DOM changes
let domChangeCount = 0
const controller = await page.on('domchange', async () => {
domChangeCount++
})
await page.goto('https://example.com/dynamic-page')
// Wait for page to settle
await new Promise(resolve => setTimeout(resolve, 5000))
await page.off(controller)
console.log(`Total DOM changes: ${domChangeCount}`)
Wait for specific request
async function waitForAPICall(page, apiPath) {
return new Promise((resolve) => {
page.on('xhr', async (event) => {
const req = event.data
if (req.url.includes(apiPath) && req.response) {
resolve(req.response)
}
})
})
}
// Click button that triggers API call
await page.click('#load-data')
// Wait for the API call to complete
const response = await waitForAPICall(page, '/api/data')
console.log('API call completed:', response.status)
Detect failed requests
const failedRequests = []
await page.on('xhr', async (event) => {
const req = event.data
if (req.response && parseInt(req.response.status) >= 400) {
failedRequests.push({
url: req.url,
method: req.method,
status: req.response.status
})
console.error(`Failed request: ${req.method} ${req.url} - ${req.response.status}`)
}
})
await page.goto('https://example.com')
console.log(`Total failed requests: ${failedRequests.length}`)
Monitor page navigation
const navigationHistory = []
await page.on('locationchange', async () => {
const url = await page.url()
navigationHistory.push({
url,
timestamp: Date.now()
})
console.log('Navigated to:', url)
})
await page.goto('https://example.com')
await page.click('#link1')
await page.click('#link2')
await page.click('#back-button')
console.log('Navigation history:')
navigationHistory.forEach((nav, i) => {
console.log(`${i + 1}. ${nav.url} at ${new Date(nav.timestamp).toISOString()}`)
})
const requestTimes = new Map()
await page.on('xhr', async (event) => {
const req = event.data
if (!requestTimes.has(req.url)) {
requestTimes.set(req.url, {
startTime: Date.now(),
url: req.url
})
}
if (req.response) {
const startTime = requestTimes.get(req.url)?.startTime
if (startTime) {
const duration = Date.now() - startTime
console.log(`${req.url} took ${duration}ms`)
}
}
})
await page.goto('https://example.com')
Handle custom events
// Add user script to dispatch custom events
await page.addUserScript(`
window.addEventListener('load', () => {
// Dispatch custom event
window.dispatchEvent(new CustomEvent('app-ready', {
detail: { version: '1.0' }
}))
})
`)
// Listen for custom events
await page.on('dispatch', async (event) => {
console.log('Custom event received:', event.type, event.data)
})
await page.goto('https://example.com')
Cleanup on page close
let eventListeners = []
async function setupListeners(page) {
const xhrController = await page.on('xhr', xhrHandler)
const domController = await page.on('domchange', domHandler)
eventListeners.push(xhrController, domController)
// Cleanup when page closes
await page.on('closed', async () => {
console.log('Page closed, cleaning up listeners')
for (const controller of eventListeners) {
await page.off(controller)
}
eventListeners = []
})
}
await setupListeners(page)
Debug mode logging
async function enableDebugMode(page) {
await page.on('xhr', async (event) => {
const req = event.data
console.log('[XHR]', req.method, req.url)
})
await page.on('started', async () => {
console.log('[NAV] Page load started')
})
await page.on('finished', async () => {
console.log('[NAV] Page load finished')
})
await page.on('locationchange', async () => {
const url = await page.url()
console.log('[NAV] Location changed:', url)
})
await page.on('domchange', async () => {
console.log('[DOM] DOM mutated')
})
console.log('Debug mode enabled')
}
await enableDebugMode(page)
await page.goto('https://example.com')
Best practices
Always remove event listeners when done:const controller = await page.on('xhr', handler)
// Do work
await page.off(controller)
Use once() for one-time events
Use once() for events you only need to handle once:await page.once('finished', async () => {
console.log('Page loaded')
})
Filter events in your handler to avoid processing irrelevant data:await page.on('xhr', async (event) => {
const req = event.data
if (!req.url.includes('/api/')) return // Skip non-API requests
// Process API request
})
Event handlers can be async, but they don’t block the page:await page.on('xhr', async (event) => {
// This is async but doesn't block page execution
await processRequest(event.data)
})
Waiting
Wait for specific requests
Requests
Make HTTP requests
JavaScript
Execute JavaScript
Page overview
Back to Page API overview