|
3 | 3 | * Copyright 2025 Google LLC |
4 | 4 | * SPDX-License-Identifier: Apache-2.0 |
5 | 5 | */ |
6 | | -import { type HTTPRequest, type ResourceType } from 'puppeteer-core'; |
| 6 | +import {type HTTPRequest, type ResourceType} from 'puppeteer-core'; |
7 | 7 |
|
8 | 8 | export const FILTERABLE_RESOURCE_TYPES = [ |
9 | | - 'document', |
10 | | - 'stylesheet', |
11 | | - 'image', |
12 | | - 'media', |
13 | | - 'font', |
14 | | - 'script', |
15 | | - 'xhr', |
16 | | - 'fetch', |
17 | | - 'prefetch', |
18 | | - 'websocket', |
19 | | - 'preflight', |
20 | | - 'other', |
| 9 | + 'document', |
| 10 | + 'stylesheet', |
| 11 | + 'image', |
| 12 | + 'media', |
| 13 | + 'font', |
| 14 | + 'script', |
| 15 | + 'xhr', |
| 16 | + 'fetch', |
| 17 | + 'prefetch', |
| 18 | + 'websocket', |
| 19 | + 'preflight', |
| 20 | + 'other', |
21 | 21 | ] as const satisfies readonly ResourceType[]; |
22 | 22 |
|
23 | 23 | export type FilterableResourceType = (typeof FILTERABLE_RESOURCE_TYPES)[number]; |
24 | 24 |
|
25 | 25 | export type NetworkRequestsListingOptions = { |
26 | | - pageSize?: number; |
27 | | - pageToken?: string; |
28 | | - requestType?: FilterableResourceType | FilterableResourceType[]; |
| 26 | + pageSize?: number; |
| 27 | + pageToken?: string; |
| 28 | + requestType?: FilterableResourceType | FilterableResourceType[]; |
29 | 29 | }; |
30 | 30 |
|
31 | 31 | export type NetworkRequestsListingResult = { |
32 | | - requests: readonly HTTPRequest[]; |
33 | | - nextPageToken?: string; |
34 | | - previousPageToken?: string; |
35 | | - startIndex: number; |
36 | | - endIndex: number; |
37 | | - invalidToken: boolean; |
38 | | - total: number; |
39 | | - appliedRequestType?: FilterableResourceType | FilterableResourceType[]; |
| 32 | + requests: readonly HTTPRequest[]; |
| 33 | + nextPageToken?: string; |
| 34 | + previousPageToken?: string; |
| 35 | + startIndex: number; |
| 36 | + endIndex: number; |
| 37 | + invalidToken: boolean; |
| 38 | + total: number; |
| 39 | + appliedRequestType?: FilterableResourceType | FilterableResourceType[]; |
40 | 40 | }; |
41 | 41 |
|
42 | 42 | const DEFAULT_PAGE_SIZE = 20; |
43 | 43 | const FILTERABLE_RESOURCE_TYPES_SET = new Set<FilterableResourceType>( |
44 | | - FILTERABLE_RESOURCE_TYPES, |
| 44 | + FILTERABLE_RESOURCE_TYPES, |
45 | 45 | ); |
46 | 46 |
|
47 | 47 | export function isFilterableResourceType( |
48 | | - value: ResourceType | string, |
| 48 | + value: ResourceType | string, |
49 | 49 | ): value is FilterableResourceType { |
50 | | - return FILTERABLE_RESOURCE_TYPES_SET.has(value as FilterableResourceType); |
| 50 | + return FILTERABLE_RESOURCE_TYPES_SET.has(value as FilterableResourceType); |
51 | 51 | } |
52 | 52 |
|
53 | 53 | export function sanitizeRequestTypeFilter( |
54 | | - requestType?: string | string[] | null, |
| 54 | + requestType?: string | string[] | null, |
55 | 55 | ): FilterableResourceType | FilterableResourceType[] | undefined { |
56 | | - if (requestType === undefined || requestType === null) { |
57 | | - return undefined; |
58 | | - } |
| 56 | + if (requestType === undefined || requestType === null) { |
| 57 | + return undefined; |
| 58 | + } |
59 | 59 |
|
60 | | - const values = Array.isArray(requestType) ? requestType : [requestType]; |
61 | | - const sanitized = values.filter(isFilterableResourceType); |
| 60 | + const values = Array.isArray(requestType) ? requestType : [requestType]; |
| 61 | + const sanitized = values.filter(isFilterableResourceType); |
62 | 62 |
|
63 | | - if (!sanitized.length) { |
64 | | - return undefined; |
65 | | - } |
| 63 | + if (!sanitized.length) { |
| 64 | + return undefined; |
| 65 | + } |
66 | 66 |
|
67 | | - return Array.isArray(requestType) ? sanitized : sanitized[0]; |
| 67 | + return Array.isArray(requestType) ? sanitized : sanitized[0]; |
68 | 68 | } |
69 | 69 |
|
70 | 70 | export function filterNetworkRequests( |
71 | | - requests: readonly HTTPRequest[], |
72 | | - requestType?: FilterableResourceType | FilterableResourceType[], |
| 71 | + requests: readonly HTTPRequest[], |
| 72 | + requestType?: FilterableResourceType | FilterableResourceType[], |
73 | 73 | ): readonly HTTPRequest[] { |
74 | | - if (!requestType) { |
75 | | - return requests; |
76 | | - } |
77 | | - |
78 | | - const normalizedTypes = new Set<FilterableResourceType>( |
79 | | - Array.isArray(requestType) ? requestType : [requestType], |
80 | | - ); |
81 | | - |
82 | | - if (!normalizedTypes.size) { |
83 | | - return requests; |
| 74 | + if (!requestType) { |
| 75 | + return requests; |
| 76 | + } |
| 77 | + |
| 78 | + const normalizedTypes = new Set<FilterableResourceType>( |
| 79 | + Array.isArray(requestType) ? requestType : [requestType], |
| 80 | + ); |
| 81 | + |
| 82 | + if (!normalizedTypes.size) { |
| 83 | + return requests; |
| 84 | + } |
| 85 | + |
| 86 | + return requests.filter(request => { |
| 87 | + const type = request.resourceType(); |
| 88 | + if (!isFilterableResourceType(type)) { |
| 89 | + return false; |
84 | 90 | } |
85 | | - |
86 | | - return requests.filter(request => { |
87 | | - const type = request.resourceType(); |
88 | | - if (!isFilterableResourceType(type)) { |
89 | | - return false; |
90 | | - } |
91 | | - return normalizedTypes.has(type); |
92 | | - }); |
| 91 | + return normalizedTypes.has(type); |
| 92 | + }); |
93 | 93 | } |
94 | 94 |
|
95 | 95 | export function paginateNetworkRequests( |
96 | | - requests: readonly HTTPRequest[], |
97 | | - options?: NetworkRequestsListingOptions, |
| 96 | + requests: readonly HTTPRequest[], |
| 97 | + options?: NetworkRequestsListingOptions, |
98 | 98 | ): NetworkRequestsListingResult { |
99 | | - const sanitizedOptions = options ?? {}; |
100 | | - const filteredRequests = filterNetworkRequests( |
101 | | - requests, |
102 | | - sanitizedOptions.requestType, |
103 | | - ); |
104 | | - const total = filteredRequests.length; |
105 | | - |
106 | | - const hasPaginationOptions = hasPagination(sanitizedOptions); |
107 | | - |
108 | | - if (!hasPaginationOptions) { |
109 | | - return { |
110 | | - requests: filteredRequests, |
111 | | - nextPageToken: undefined, |
112 | | - previousPageToken: undefined, |
113 | | - startIndex: 0, |
114 | | - endIndex: total, |
115 | | - invalidToken: false, |
116 | | - total, |
117 | | - appliedRequestType: sanitizedOptions.requestType, |
118 | | - }; |
119 | | - } |
120 | | - |
121 | | - const pageSize = validatePageSize(sanitizedOptions.pageSize, total); |
122 | | - const { startIndex, invalidToken } = resolveStartIndex( |
123 | | - sanitizedOptions.pageToken, |
124 | | - total, |
125 | | - ); |
| 99 | + const sanitizedOptions = options ?? {}; |
| 100 | + const filteredRequests = filterNetworkRequests( |
| 101 | + requests, |
| 102 | + sanitizedOptions.requestType, |
| 103 | + ); |
| 104 | + const total = filteredRequests.length; |
126 | 105 |
|
127 | | - const pageRequests = filteredRequests.slice(startIndex, startIndex + pageSize); |
128 | | - const endIndex = startIndex + pageRequests.length; |
129 | | - |
130 | | - const nextPageToken = endIndex < total ? String(endIndex) : undefined; |
131 | | - const previousPageToken = |
132 | | - startIndex > 0 ? String(Math.max(startIndex - pageSize, 0)) : undefined; |
| 106 | + const hasPaginationOptions = hasPagination(sanitizedOptions); |
133 | 107 |
|
| 108 | + if (!hasPaginationOptions) { |
134 | 109 | return { |
135 | | - requests: pageRequests, |
136 | | - nextPageToken, |
137 | | - previousPageToken, |
138 | | - startIndex, |
139 | | - endIndex, |
140 | | - invalidToken, |
141 | | - total, |
142 | | - appliedRequestType: sanitizedOptions.requestType, |
| 110 | + requests: filteredRequests, |
| 111 | + nextPageToken: undefined, |
| 112 | + previousPageToken: undefined, |
| 113 | + startIndex: 0, |
| 114 | + endIndex: total, |
| 115 | + invalidToken: false, |
| 116 | + total, |
| 117 | + appliedRequestType: sanitizedOptions.requestType, |
143 | 118 | }; |
| 119 | + } |
| 120 | + |
| 121 | + const pageSize = validatePageSize(sanitizedOptions.pageSize, total); |
| 122 | + const {startIndex, invalidToken} = resolveStartIndex( |
| 123 | + sanitizedOptions.pageToken, |
| 124 | + total, |
| 125 | + ); |
| 126 | + |
| 127 | + const pageRequests = filteredRequests.slice( |
| 128 | + startIndex, |
| 129 | + startIndex + pageSize, |
| 130 | + ); |
| 131 | + const endIndex = startIndex + pageRequests.length; |
| 132 | + |
| 133 | + const nextPageToken = endIndex < total ? String(endIndex) : undefined; |
| 134 | + const previousPageToken = |
| 135 | + startIndex > 0 ? String(Math.max(startIndex - pageSize, 0)) : undefined; |
| 136 | + |
| 137 | + return { |
| 138 | + requests: pageRequests, |
| 139 | + nextPageToken, |
| 140 | + previousPageToken, |
| 141 | + startIndex, |
| 142 | + endIndex, |
| 143 | + invalidToken, |
| 144 | + total, |
| 145 | + appliedRequestType: sanitizedOptions.requestType, |
| 146 | + }; |
144 | 147 | } |
145 | 148 |
|
146 | 149 | function hasPagination(options: NetworkRequestsListingOptions): boolean { |
147 | | - return ( |
148 | | - options.pageSize !== undefined || |
149 | | - (options.pageToken !== undefined && options.pageToken !== null) |
150 | | - ); |
| 150 | + return ( |
| 151 | + options.pageSize !== undefined || |
| 152 | + (options.pageToken !== undefined && options.pageToken !== null) |
| 153 | + ); |
151 | 154 | } |
152 | 155 |
|
153 | 156 | function validatePageSize(pageSize: number | undefined, total: number): number { |
154 | | - if (pageSize === undefined) { |
155 | | - return total || DEFAULT_PAGE_SIZE; |
156 | | - } |
157 | | - if (!Number.isInteger(pageSize) || pageSize <= 0) { |
158 | | - return DEFAULT_PAGE_SIZE; |
159 | | - } |
160 | | - return Math.min(pageSize, Math.max(total, 1)); |
| 157 | + if (pageSize === undefined) { |
| 158 | + return total || DEFAULT_PAGE_SIZE; |
| 159 | + } |
| 160 | + if (!Number.isInteger(pageSize) || pageSize <= 0) { |
| 161 | + return DEFAULT_PAGE_SIZE; |
| 162 | + } |
| 163 | + return Math.min(pageSize, Math.max(total, 1)); |
161 | 164 | } |
162 | 165 |
|
163 | 166 | function resolveStartIndex( |
164 | | - pageToken: string | undefined, |
165 | | - total: number, |
| 167 | + pageToken: string | undefined, |
| 168 | + total: number, |
166 | 169 | ): { |
167 | | - startIndex: number; |
168 | | - invalidToken: boolean; |
| 170 | + startIndex: number; |
| 171 | + invalidToken: boolean; |
169 | 172 | } { |
170 | | - if (pageToken === undefined || pageToken === null) { |
171 | | - return { startIndex: 0, invalidToken: false }; |
172 | | - } |
| 173 | + if (pageToken === undefined || pageToken === null) { |
| 174 | + return {startIndex: 0, invalidToken: false}; |
| 175 | + } |
173 | 176 |
|
174 | | - const parsed = Number.parseInt(pageToken, 10); |
175 | | - if (Number.isNaN(parsed) || parsed < 0 || parsed >= total) { |
176 | | - return { startIndex: 0, invalidToken: total > 0 }; |
177 | | - } |
| 177 | + const parsed = Number.parseInt(pageToken, 10); |
| 178 | + if (Number.isNaN(parsed) || parsed < 0 || parsed >= total) { |
| 179 | + return {startIndex: 0, invalidToken: total > 0}; |
| 180 | + } |
178 | 181 |
|
179 | | - return { startIndex: parsed, invalidToken: false }; |
| 182 | + return {startIndex: parsed, invalidToken: false}; |
180 | 183 | } |
181 | | - |
182 | | - |
|
0 commit comments