From 9542eefc3e7b172948d6dd9f2cad228e05e8d656 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 27 Feb 2025 01:05:09 +0100 Subject: [PATCH 1/5] added request and requestFinished events to APIRequestContext --- docs/src/api/class-apirequestcontext.md | 12 ++++ packages/playwright-core/src/client/events.ts | 5 ++ packages/playwright-core/src/client/fetch.ts | 17 +++++- .../playwright-core/src/protocol/validator.ts | 41 ++++++++++++- .../src/protocol/validatorPrimitives.ts | 7 +++ .../server/dispatchers/networkDispatchers.ts | 12 +++- packages/playwright-core/src/server/fetch.ts | 2 + packages/playwright-core/types/types.d.ts | 13 ++++ packages/protocol/src/channels.d.ts | 43 +++++++++++++- packages/protocol/src/protocol.yml | 51 +++++++++++++++- .../apirequestcontext-network-event.spec.ts | 59 +++++++++++++++++++ utils/generate_channels.js | 5 +- 12 files changed, 258 insertions(+), 9 deletions(-) create mode 100644 tests/library/apirequestcontext-network-event.spec.ts diff --git a/docs/src/api/class-apirequestcontext.md b/docs/src/api/class-apirequestcontext.md index 165b9f3bd6..ab286b3163 100644 --- a/docs/src/api/class-apirequestcontext.md +++ b/docs/src/api/class-apirequestcontext.md @@ -120,6 +120,18 @@ with sync_playwright() as p: assert await response.body() == '{"status": "ok"}' ``` +## event: APIRequestContext.apiRequest +- argument: <[APIRequestEvent]> + +Emitted when a request is issued from any pages created through this context. +The [APIRequestEvent] object is read-only. + +## event: APIRequestContext.apiRequestFinished +- argument: <[APIRequestFinishedEvent]> + +Emitted when a request finishes successfully after downloading the response body. For a successful response, the +sequence of events is `request` and `requestfinished`. + ## method: APIRequestContext.createFormData * since: v1.23 * langs: csharp diff --git a/packages/playwright-core/src/client/events.ts b/packages/playwright-core/src/client/events.ts index a074b26f3d..de5b0a67a6 100644 --- a/packages/playwright-core/src/client/events.ts +++ b/packages/playwright-core/src/client/events.ts @@ -50,6 +50,11 @@ export const Events = { RequestFinished: 'requestfinished', }, + APIRequestContext: { + APIRequest: 'apiRequest', + APIRequestFinished: 'apiRequestfinished', + }, + BrowserServer: { Close: 'close', }, diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index dada11ed30..72c8c1900f 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -14,6 +14,8 @@ * limitations under the License. */ +import { APIRequestEvent, APIRequestFinishedEvent } from 'playwright-core/lib/server/fetch'; + import { toClientCertificatesProtocol } from './browserContext'; import { ChannelOwner } from './channelOwner'; import { TargetClosedError, isTargetClosedError } from './errors'; @@ -23,6 +25,7 @@ import { assert } from '../utils/isomorphic/assert'; import { mkdirIfNeeded } from './fileUtils'; import { headersObjectToArray } from '../utils/isomorphic/headers'; import { isString } from '../utils/isomorphic/rtti'; +import { Events } from './events'; import type { Playwright } from './playwright'; import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState } from './types'; @@ -98,7 +101,11 @@ export class APIRequestContext extends ChannelOwner { + this._onRequest(request); + }); + this._channel.on('apiRequestFinished', params => this._onRequestFinished(params)); } async [Symbol.asyncDispose]() { @@ -269,6 +276,14 @@ export class APIRequestContext extends ChannelOwner { diff --git a/packages/playwright-core/src/protocol/validator.ts b/packages/playwright-core/src/protocol/validator.ts index bd36e905b4..0ef6abd4cc 100644 --- a/packages/playwright-core/src/protocol/validator.ts +++ b/packages/playwright-core/src/protocol/validator.ts @@ -16,7 +16,7 @@ // This file is generated by generate_channels.js, do not edit manually. -import { scheme, tOptional, tObject, tBoolean, tNumber, tString, tAny, tEnum, tArray, tBinary, tChannel, tType } from './validatorPrimitives'; +import { scheme, tOptional, tObject, tBoolean, tNumber, tString, tAny, tEnum, tArray, tBinary, tChannel, tType, tURL } from './validatorPrimitives'; export type { Validator, ValidatorContext } from './validatorPrimitives'; export { ValidationError, findValidator, maybeFindValidator, createMetadataValidator } from './validatorPrimitives'; @@ -201,7 +201,44 @@ scheme.FormField = tObject({ })), }); scheme.APIRequestContextInitializer = tObject({ - tracing: tChannel(['Tracing']), + tracing: tOptional(tChannel(['Tracing'])), +}); +scheme.APIRequestContextApiRequestEvent = tObject({ + guid: tString, + url: tURL, + method: tString, + headers: tAny, + cookies: tArray(tType('NameValue')), + postData: tOptional(tBinary), +}); +scheme.APIRequestContextApiRequestFinishedEvent = tObject({ + requestEvent: tObject({ + guid: tString, + url: tURL, + method: tString, + headers: tAny, + cookies: tArray(tType('NameValue')), + }), + httpVersion: tString, + headers: tAny, + cookies: tArray(tType('NetworkCookie')), + rawHeaders: tArray(tString), + statusCode: tNumber, + statusMessage: tString, + body: tOptional(tBinary), + timings: tObject({ + blocked: tOptional(tNumber), + dns: tOptional(tNumber), + connect: tOptional(tNumber), + send: tNumber, + wait: tNumber, + receive: tNumber, + ssl: tOptional(tNumber), + comment: tOptional(tString), + }), + serverIPAddress: tOptional(tString), + serverPort: tOptional(tNumber), + securityDetails: tOptional(tType('SecurityDetails')), }); scheme.APIRequestContextFetchParams = tObject({ url: tString, diff --git a/packages/playwright-core/src/protocol/validatorPrimitives.ts b/packages/playwright-core/src/protocol/validatorPrimitives.ts index eadd2e014e..c49bddad21 100644 --- a/packages/playwright-core/src/protocol/validatorPrimitives.ts +++ b/packages/playwright-core/src/protocol/validatorPrimitives.ts @@ -128,6 +128,13 @@ export const tEnum = (e: string[]): Validator => { return arg; }; }; +export const tURL = (arg: any, path: string, context: ValidatorContext) => { + if (arg instanceof URL) + return arg; + if (typeof arg === 'string') + return new URL(arg); + throw new ValidationError(`${path}: expected URL, got ${typeof arg}`); +}; export const tChannel = (names: '*' | string[]): Validator => { return (arg: any, path: string, context: ValidatorContext) => { return context.tChannelImpl(names, arg, path, context); diff --git a/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts b/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts index c6c783f390..1e10a559a9 100644 --- a/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts +++ b/packages/playwright-core/src/server/dispatchers/networkDispatchers.ts @@ -19,11 +19,11 @@ import { Dispatcher, existingDispatcher } from './dispatcher'; import { FrameDispatcher } from './frameDispatcher'; import { WorkerDispatcher } from './pageDispatcher'; import { TracingDispatcher } from './tracingDispatcher'; +import { APIRequestContext, APIRequestEvent, APIRequestFinishedEvent } from '../fetch'; +import { BrowserContextDispatcher } from './browserContextDispatcher'; -import type { APIRequestContext } from '../fetch'; import type { CallMetadata } from '../instrumentation'; import type { Request, Response, Route } from '../network'; -import type { BrowserContextDispatcher } from './browserContextDispatcher'; import type { RootDispatcher } from './dispatcher'; import type { PageDispatcher } from './pageDispatcher'; import type * as channels from '@protocol/channels'; @@ -193,9 +193,17 @@ export class APIRequestContextDispatcher extends Dispatcher { + this._dispatchEvent('apiRequest', request); + }); + this.addObjectListener(APIRequestContext.Events.RequestFinished, (request: APIRequestFinishedEvent) => { + this._dispatchEvent('apiRequestFinished', request); + }); } + async storageState(params: channels.APIRequestContextStorageStateParams): Promise { return this._object.storageState(params.indexedDB); } diff --git a/packages/playwright-core/src/server/fetch.ts b/packages/playwright-core/src/server/fetch.ts index 984d5e3067..3a3279bc72 100644 --- a/packages/playwright-core/src/server/fetch.ts +++ b/packages/playwright-core/src/server/fetch.ts @@ -63,6 +63,7 @@ type FetchRequestOptions = { type HeadersObject = Readonly<{ [name: string]: string }>; export type APIRequestEvent = { + guid: string, url: URL, method: string, headers: HeadersObject, @@ -294,6 +295,7 @@ export abstract class APIRequestContext extends SdkObject { return { name, value }; }) || []; const requestEvent: APIRequestEvent = { + guid: createGuid(), url, method: options.method!, headers: options.headers, diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 874f072035..738bffd770 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -19,6 +19,7 @@ import { Readable } from 'stream'; import { ReadStream } from 'fs'; import { Protocol } from './protocol'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs'; +import {APIRequestEvent, APIRequestFinishedEvent} from "playwright-core/lib/server/fetch"; type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state?: 'visible'|'attached'; @@ -18665,6 +18666,18 @@ export interface APIRequestContext { }>; [Symbol.asyncDispose](): Promise; + + + /** + * Emitted when a request is issued from API request context. The event will be emitted after the request is issued + */ + on(event: 'apiRequest', listener: (request: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. + */ + on(event: 'apiRequestfinished', listener: (request: APIRequestFinishedEvent) => any): this; } /** diff --git a/packages/protocol/src/channels.d.ts b/packages/protocol/src/channels.d.ts index 117f0e287e..879c2391e8 100644 --- a/packages/protocol/src/channels.d.ts +++ b/packages/protocol/src/channels.d.ts @@ -337,9 +337,11 @@ export type FormField = { // ----------- APIRequestContext ----------- export type APIRequestContextInitializer = { - tracing: TracingChannel, + tracing?: TracingChannel, }; export interface APIRequestContextEventTarget { + on(event: 'apiRequest', callback: (params: APIRequestContextApiRequestEvent) => void): this; + on(event: 'apiRequestFinished', callback: (params: APIRequestContextApiRequestFinishedEvent) => void): this; } export interface APIRequestContextChannel extends APIRequestContextEventTarget, Channel { _type_APIRequestContext: boolean; @@ -350,6 +352,43 @@ export interface APIRequestContextChannel extends APIRequestContextEventTarget, disposeAPIResponse(params: APIRequestContextDisposeAPIResponseParams, metadata?: CallMetadata): Promise; dispose(params: APIRequestContextDisposeParams, metadata?: CallMetadata): Promise; } +export type APIRequestContextApiRequestEvent = { + guid: string, + url: URL, + method: string, + headers: any, + cookies: NameValue[], + postData?: Binary, +}; +export type APIRequestContextApiRequestFinishedEvent = { + requestEvent: { + guid: string, + url: URL, + method: string, + headers: any, + cookies: NameValue[], + }, + httpVersion: string, + headers: any, + cookies: NetworkCookie[], + rawHeaders: string[], + statusCode: number, + statusMessage: string, + body?: Binary, + timings: { + blocked?: number, + dns?: number, + connect?: number, + send: number, + wait: number, + receive: number, + ssl?: number, + comment?: string, + }, + serverIPAddress?: string, + serverPort?: number, + securityDetails?: SecurityDetails, +}; export type APIRequestContextFetchParams = { url: string, encodedParams?: string, @@ -428,6 +467,8 @@ export type APIRequestContextDisposeOptions = { export type APIRequestContextDisposeResult = void; export interface APIRequestContextEvents { + 'apiRequest': APIRequestContextApiRequestEvent; + 'apiRequestFinished': APIRequestContextApiRequestFinishedEvent; } export type APIResponse = { diff --git a/packages/protocol/src/protocol.yml b/packages/protocol/src/protocol.yml index 1044f8ccb2..9ac9973adf 100644 --- a/packages/protocol/src/protocol.yml +++ b/packages/protocol/src/protocol.yml @@ -313,7 +313,6 @@ RecordHarOptions: urlRegexSource: string? urlRegexFlags: string? - FormField: type: object properties: @@ -330,7 +329,7 @@ APIRequestContext: type: interface initializer: - tracing: Tracing + tracing: Tracing? commands: @@ -394,6 +393,54 @@ APIRequestContext: parameters: reason: string? + events: + apiRequest: + parameters: + guid: string + url: URL + method: string + headers: json + cookies: + type: array + items: NameValue + postData: binary? + apiRequestFinished: + parameters: + requestEvent: + type: object + properties: + guid: string + url: URL + method: string + headers: json + cookies: + type: array + items: NameValue + httpVersion: string + headers: json + cookies: + type: array + items: NetworkCookie + rawHeaders: + type: array + items: string + statusCode: number + statusMessage: string + body: binary? + timings: + type: object + properties: + blocked: number? + dns: number? + connect: number? + send: number + wait: number + receive: number + ssl: number? + comment: string? + serverIPAddress: string? + serverPort: number? + securityDetails: SecurityDetails? APIResponse: type: object diff --git a/tests/library/apirequestcontext-network-event.spec.ts b/tests/library/apirequestcontext-network-event.spec.ts new file mode 100644 index 0000000000..6f8c6f1da6 --- /dev/null +++ b/tests/library/apirequestcontext-network-event.spec.ts @@ -0,0 +1,59 @@ +/** + * Copyright 2018 Google Inc. All rights reserved. + * Modifications copyright (c) Microsoft Corporation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { browserTest as it, expect } from '../config/browserTest'; +import { APIRequestEvent, APIRequestFinishedEvent } from 'playwright-core/src/server/fetch'; + +it('APIRequestContext.Events.Request', async ({ context, server }) => { + const requests: APIRequestEvent[] = []; + context.request.on('apiRequest', request => { + requests.push(request); + }); + await context.request.fetch(server.EMPTY_PAGE); + + await setTimeout(() => {}, 100); + + const urls = requests.map(r => r.url.toString()); + expect(urls).toEqual([ + server.EMPTY_PAGE, + ]); +}); + + +it('APIRequestContext.Events.RequestFinished', async ({ context, server }) => { + + const finishedRequests: APIRequestFinishedEvent[] = []; + + context.request.on('apiRequestfinished', request => finishedRequests.push(request)); + await context.request.fetch(server.EMPTY_PAGE); + + const request = finishedRequests[0]; + + expect(request.requestEvent.url.toString()).toBe(server.EMPTY_PAGE); + expect(request.timings.send).toBeTruthy(); +}); + +it('should fire events in proper order', async ({ context, server }) => { + const events: string[] = []; + context.request.on('apiRequest', () => events.push('apiRequest')); + context.request.on('apiRequestfinished', () => events.push('apiRequestfinished')); + await context.request.fetch(server.EMPTY_PAGE); + expect(events).toEqual([ + 'apiRequest', + 'apiRequestfinished' + ]); +}); diff --git a/utils/generate_channels.js b/utils/generate_channels.js index 827250ad8c..6270115d0c 100755 --- a/utils/generate_channels.js +++ b/utils/generate_channels.js @@ -51,6 +51,9 @@ function inlineType(type, indent, wrapEnums = false) { } if (type === 'Channel') return { ts: `Channel`, scheme: `tChannel('*')`, optional }; + if(type === 'URL'){ + return { ts: 'URL', scheme: `tURL`, optional }; + } return { ts: type, scheme: `tType('${type}')`, optional }; } if (type.type.startsWith('array')) { @@ -153,7 +156,7 @@ const validator_ts = [ // This file is generated by ${path.basename(__filename)}, do not edit manually. -import { scheme, tOptional, tObject, tBoolean, tNumber, tString, tAny, tEnum, tArray, tBinary, tChannel, tType } from './validatorPrimitives'; +import { scheme, tOptional, tObject, tBoolean, tNumber, tString, tAny, tEnum, tArray, tBinary, tChannel, tType, tURL } from './validatorPrimitives'; export type { Validator, ValidatorContext } from './validatorPrimitives'; export { ValidationError, findValidator, maybeFindValidator, createMetadataValidator } from './validatorPrimitives'; `]; From 4e4735c49a273fd8fba25e3a9942d5ce9b330cfe Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 27 Feb 2025 10:48:17 +0100 Subject: [PATCH 2/5] add types in client --- packages/playwright-client/types/types.d.ts | 12 ++++++++++++ packages/playwright-core/types/types.d.ts | 1 - 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 874f072035..7328793c2e 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -19,6 +19,7 @@ import { Readable } from 'stream'; import { ReadStream } from 'fs'; import { Protocol } from './protocol'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs'; +import {APIRequestEvent, APIRequestFinishedEvent} from "playwright-core/lib/server/fetch"; type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state?: 'visible'|'attached'; @@ -18665,6 +18666,17 @@ export interface APIRequestContext { }>; [Symbol.asyncDispose](): Promise; + + /** + * Emitted when a request is issued from API request context. The event will be emitted after the request is issued + */ + on(event: 'apiRequest', listener: (request: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes successfully after downloading the response body. For a successful response, the + * sequence of events is `request`, `response` and `requestfinished`. + */ + on(event: 'apiRequestfinished', listener: (request: APIRequestFinishedEvent) => any): this; } /** diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 738bffd770..7328793c2e 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -18667,7 +18667,6 @@ export interface APIRequestContext { [Symbol.asyncDispose](): Promise; - /** * Emitted when a request is issued from API request context. The event will be emitted after the request is issued */ From 82143fc35cdea0397c57932f6467465190de5749 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 27 Feb 2025 11:52:44 +0100 Subject: [PATCH 3/5] update docs --- docs/src/api/class-apirequestcontext.md | 26 ++++--- ...-workers-experimental-network-events-js.md | 3 + packages/playwright-client/types/types.d.ts | 78 ++++++++++++++++--- packages/playwright-core/src/client/events.ts | 4 +- packages/playwright-core/types/types.d.ts | 78 ++++++++++++++++--- .../apirequestcontext-network-event.spec.ts | 12 +-- 6 files changed, 157 insertions(+), 44 deletions(-) diff --git a/docs/src/api/class-apirequestcontext.md b/docs/src/api/class-apirequestcontext.md index ab286b3163..d84c8a335c 100644 --- a/docs/src/api/class-apirequestcontext.md +++ b/docs/src/api/class-apirequestcontext.md @@ -120,18 +120,6 @@ with sync_playwright() as p: assert await response.body() == '{"status": "ok"}' ``` -## event: APIRequestContext.apiRequest -- argument: <[APIRequestEvent]> - -Emitted when a request is issued from any pages created through this context. -The [APIRequestEvent] object is read-only. - -## event: APIRequestContext.apiRequestFinished -- argument: <[APIRequestFinishedEvent]> - -Emitted when a request finishes successfully after downloading the response body. For a successful response, the -sequence of events is `request` and `requestfinished`. - ## method: APIRequestContext.createFormData * since: v1.23 * langs: csharp @@ -927,3 +915,17 @@ Returns storage state for this request context, contains current cookies and loc - `indexedDB` ? Defaults to `true`. Set to `false` to omit IndexedDB from snapshot. + +## event: APIRequestContext.apiRequest +* since: v1.51 +- argument: <[APIRequestEvent]> + +Emitted when a request is issued from any requests created through this context. +The [APIRequestEvent] object is read-only. + +## event: APIRequestContext.apiRequestFinished +* since: v1.51 +- argument: <[APIRequestFinishedEvent]> + +Emitted when a request finishes in any requests created through this context. The +sequence of events is `apirequest` and `apirequestfinished` diff --git a/docs/src/service-workers-experimental-network-events-js.md b/docs/src/service-workers-experimental-network-events-js.md index 43f32d7e61..50a59dfa83 100644 --- a/docs/src/service-workers-experimental-network-events-js.md +++ b/docs/src/service-workers-experimental-network-events-js.md @@ -67,6 +67,9 @@ Additionally, any network request made by the **Page** (including its sub-[Frame * [`method: Request.serviceWorker`] will be set to `null`, and [`method: Request.frame`] will return the [Frame] * [`method: Response.fromServiceWorker`] will return `true` (if a Service Worker's fetch handler was registered) +Any requests made by **APIRequestContext** will have +* [`event: APIRequestContext.apiRequest`] and its corresponding event ([`event: BrowserContext.apiRequestFinished`]) + Many Service Worker implementations simply execute the request from the page (possibly with some custom caching/offline logic omitted for simplicity): ```js title="transparent-service-worker.js" diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index 7328793c2e..d5f746ad23 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -19,7 +19,6 @@ import { Readable } from 'stream'; import { ReadStream } from 'fs'; import { Protocol } from './protocol'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs'; -import {APIRequestEvent, APIRequestFinishedEvent} from "playwright-core/lib/server/fetch"; type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state?: 'visible'|'attached'; @@ -17849,6 +17848,72 @@ export interface APIRequest { * `APIRequestContext` object will have its own isolated cookie storage. */ export interface APIRequestContext { + /** + * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is + * read-only. + */ + on(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes in any requests created through this context. The sequence of events is + * `apirequest` and `apirequestfinished` + */ + on(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. + */ + once(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. + */ + once(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is + * read-only. + */ + addListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes in any requests created through this context. The sequence of events is + * `apirequest` and `apirequestfinished` + */ + addListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is + * read-only. + */ + prependListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes in any requests created through this context. The sequence of events is + * `apirequest` and `apirequestfinished` + */ + prependListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + /** * Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its * response. The method will populate request cookies from the context and update context cookies from the response. @@ -18666,17 +18731,6 @@ export interface APIRequestContext { }>; [Symbol.asyncDispose](): Promise; - - /** - * Emitted when a request is issued from API request context. The event will be emitted after the request is issued - */ - on(event: 'apiRequest', listener: (request: APIRequestEvent) => any): this; - - /** - * Emitted when a request finishes successfully after downloading the response body. For a successful response, the - * sequence of events is `request`, `response` and `requestfinished`. - */ - on(event: 'apiRequestfinished', listener: (request: APIRequestFinishedEvent) => any): this; } /** diff --git a/packages/playwright-core/src/client/events.ts b/packages/playwright-core/src/client/events.ts index de5b0a67a6..47d804c823 100644 --- a/packages/playwright-core/src/client/events.ts +++ b/packages/playwright-core/src/client/events.ts @@ -51,8 +51,8 @@ export const Events = { }, APIRequestContext: { - APIRequest: 'apiRequest', - APIRequestFinished: 'apiRequestfinished', + APIRequest: 'apirequest', + APIRequestFinished: 'apirequestfinished', }, BrowserServer: { diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index 7328793c2e..d5f746ad23 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -19,7 +19,6 @@ import { Readable } from 'stream'; import { ReadStream } from 'fs'; import { Protocol } from './protocol'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs'; -import {APIRequestEvent, APIRequestFinishedEvent} from "playwright-core/lib/server/fetch"; type PageWaitForSelectorOptionsNotHidden = PageWaitForSelectorOptions & { state?: 'visible'|'attached'; @@ -17849,6 +17848,72 @@ export interface APIRequest { * `APIRequestContext` object will have its own isolated cookie storage. */ export interface APIRequestContext { + /** + * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is + * read-only. + */ + on(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes in any requests created through this context. The sequence of events is + * `apirequest` and `apirequestfinished` + */ + on(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. + */ + once(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. + */ + once(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is + * read-only. + */ + addListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes in any requests created through this context. The sequence of events is + * `apirequest` and `apirequestfinished` + */ + addListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + removeListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Removes an event listener added by `on` or `addListener`. + */ + off(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + + /** + * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is + * read-only. + */ + prependListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + + /** + * Emitted when a request finishes in any requests created through this context. The sequence of events is + * `apirequest` and `apirequestfinished` + */ + prependListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + /** * Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its * response. The method will populate request cookies from the context and update context cookies from the response. @@ -18666,17 +18731,6 @@ export interface APIRequestContext { }>; [Symbol.asyncDispose](): Promise; - - /** - * Emitted when a request is issued from API request context. The event will be emitted after the request is issued - */ - on(event: 'apiRequest', listener: (request: APIRequestEvent) => any): this; - - /** - * Emitted when a request finishes successfully after downloading the response body. For a successful response, the - * sequence of events is `request`, `response` and `requestfinished`. - */ - on(event: 'apiRequestfinished', listener: (request: APIRequestFinishedEvent) => any): this; } /** diff --git a/tests/library/apirequestcontext-network-event.spec.ts b/tests/library/apirequestcontext-network-event.spec.ts index 6f8c6f1da6..c0acb9f946 100644 --- a/tests/library/apirequestcontext-network-event.spec.ts +++ b/tests/library/apirequestcontext-network-event.spec.ts @@ -20,7 +20,7 @@ import { APIRequestEvent, APIRequestFinishedEvent } from 'playwright-core/src/se it('APIRequestContext.Events.Request', async ({ context, server }) => { const requests: APIRequestEvent[] = []; - context.request.on('apiRequest', request => { + context.request.on('apirequest', request => { requests.push(request); }); await context.request.fetch(server.EMPTY_PAGE); @@ -38,7 +38,7 @@ it('APIRequestContext.Events.RequestFinished', async ({ context, server }) => { const finishedRequests: APIRequestFinishedEvent[] = []; - context.request.on('apiRequestfinished', request => finishedRequests.push(request)); + context.request.on('apirequestfinished', request => finishedRequests.push(request)); await context.request.fetch(server.EMPTY_PAGE); const request = finishedRequests[0]; @@ -49,11 +49,11 @@ it('APIRequestContext.Events.RequestFinished', async ({ context, server }) => { it('should fire events in proper order', async ({ context, server }) => { const events: string[] = []; - context.request.on('apiRequest', () => events.push('apiRequest')); - context.request.on('apiRequestfinished', () => events.push('apiRequestfinished')); + context.request.on('apirequest', () => events.push('apirequest')); + context.request.on('apirequestfinished', () => events.push('apirequestfinished')); await context.request.fetch(server.EMPTY_PAGE); expect(events).toEqual([ - 'apiRequest', - 'apiRequestfinished' + 'apirequest', + 'apirequestfinished' ]); }); From a2778c7e749e92d3e32d1a84ddd84896ac9fc4b3 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 27 Feb 2025 12:25:30 +0100 Subject: [PATCH 4/5] lint --- docs/src/service-workers-experimental-network-events-js.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/service-workers-experimental-network-events-js.md b/docs/src/service-workers-experimental-network-events-js.md index 50a59dfa83..84a4973f4d 100644 --- a/docs/src/service-workers-experimental-network-events-js.md +++ b/docs/src/service-workers-experimental-network-events-js.md @@ -68,7 +68,7 @@ Additionally, any network request made by the **Page** (including its sub-[Frame * [`method: Response.fromServiceWorker`] will return `true` (if a Service Worker's fetch handler was registered) Any requests made by **APIRequestContext** will have -* [`event: APIRequestContext.apiRequest`] and its corresponding event ([`event: BrowserContext.apiRequestFinished`]) +* [`event: APIRequestContext.apiRequest`] and its corresponding event ([`event: APIRequestContext.apiRequestFinished`]) Many Service Worker implementations simply execute the request from the page (possibly with some custom caching/offline logic omitted for simplicity): From e040b42ca3bcefc47b36cd021464a2f5dc1ca6f1 Mon Sep 17 00:00:00 2001 From: Shahzad Date: Thu, 27 Feb 2025 14:28:24 +0100 Subject: [PATCH 5/5] update docs --- docs/src/api/class-apirequestcontext.md | 58 +- packages/playwright-client/types/types.d.ts | 1093 +++++++++++++++++- packages/playwright-core/src/client/fetch.ts | 2 +- packages/playwright-core/types/types.d.ts | 1093 +++++++++++++++++- utils/generate_types/overrides.d.ts | 1 + 5 files changed, 2220 insertions(+), 27 deletions(-) diff --git a/docs/src/api/class-apirequestcontext.md b/docs/src/api/class-apirequestcontext.md index d84c8a335c..ff51ae02ab 100644 --- a/docs/src/api/class-apirequestcontext.md +++ b/docs/src/api/class-apirequestcontext.md @@ -918,14 +918,68 @@ Defaults to `true`. Set to `false` to omit IndexedDB from snapshot. ## event: APIRequestContext.apiRequest * since: v1.51 -- argument: <[APIRequestEvent]> +- argument: <[Object]> + - `guid` <[string]> request GUID + - `url` <[URL]> request URL + - `method` <[string]> request method + - `headers` <[Object]<[string], [string]>> request headers + - `cookies` <[Array]<[Object]>> + - `name` <[string]> + - `value` <[string]> + - `postData` ?<[Buffer]> request post data Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is read-only. ## event: APIRequestContext.apiRequestFinished * since: v1.51 -- argument: <[APIRequestFinishedEvent]> +- argument: <[Object]> + - `httpVersion` <[string]> HTTP version + - `rawHeaders` <[Array]<[string]>> raw headers + - `statusCode` <[int]> status code + - `statusMessage` <[string]> status message + - `body` ?<[Buffer]> response body + - `headers` <[IncomingHttpHeaders]>> response headers + - `serverIPAddress` ?<[string]> server IP address + - `serverPort` ?<[int]> server port + - `timings` <[Object]> + - `blocked` ?<[int]> + - `dns` ?<[int]> + - `connect` ?<[int]> + - `send` <[int]> + - `wait` <[int]> + - `receive` <[int]> + - `ssl` ?<[int]> + - `comment` ?<[string]> + - `cookies` <[Array]<[Object]>> + - `name` <[string]> + - `value` <[string]> + - `domain` <[string]> + - `path` <[string]> + - `expires` <[float]> Unix time in seconds. + - `httpOnly` <[boolean]> + - `secure` <[boolean]> + - `sameSite` <[SameSiteAttribute]<"Strict"|"Lax"|"None">> + - `securityDetails` ?<[Object]> security details + - `issuer` ?<[string]> Common Name component of the Issuer field. + from the certificate. This should only be used for informational purposes. Optional. + - `protocol` ?<[string]> The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + - `subjectName` ?<[string]> Common Name component of the Subject + field from the certificate. This should only be used for informational purposes. Optional. + - `validFrom` ?<[float]> Unix timestamp (in seconds) specifying + when this cert becomes valid. Optional. + - `validTo` ?<[float]> Unix timestamp (in seconds) specifying + when this cert becomes invalid. Optional. + - `requestEvent` <[Object]> request event object + - `guid` <[string]> request GUID + - `url` <[URL]> request URL + - `method` <[string]> request method + - `headers` <[Object]<[string], [string]>> request headers + - `cookies` <[Array]<[Object]>> + - `name` <[string]> + - `value` <[string]> + - `postData` ?<[Buffer]> request post data + Emitted when a request finishes in any requests created through this context. The sequence of events is `apirequest` and `apirequestfinished` diff --git a/packages/playwright-client/types/types.d.ts b/packages/playwright-client/types/types.d.ts index d5f746ad23..e516ee2b3c 100644 --- a/packages/playwright-client/types/types.d.ts +++ b/packages/playwright-client/types/types.d.ts @@ -17,6 +17,7 @@ import { ChildProcess } from 'child_process'; import { Readable } from 'stream'; import { ReadStream } from 'fs'; +import type { IncomingHttpHeaders } from 'http'; import { Protocol } from './protocol'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs'; @@ -17852,67 +17853,1135 @@ export interface APIRequestContext { * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is * read-only. */ - on(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + on(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Emitted when a request finishes in any requests created through this context. The sequence of events is * `apirequest` and `apirequestfinished` */ - on(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + on(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + once(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + once(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is * read-only. */ - addListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + addListener(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Emitted when a request finishes in any requests created through this context. The sequence of events is * `apirequest` and `apirequestfinished` */ - addListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + addListener(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + removeListener(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + removeListener(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + off(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + off(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is * read-only. */ - prependListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + prependListener(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Emitted when a request finishes in any requests created through this context. The sequence of events is * `apirequest` and `apirequestfinished` */ - prependListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + prependListener(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its diff --git a/packages/playwright-core/src/client/fetch.ts b/packages/playwright-core/src/client/fetch.ts index 72c8c1900f..55591ac25b 100644 --- a/packages/playwright-core/src/client/fetch.ts +++ b/packages/playwright-core/src/client/fetch.ts @@ -14,7 +14,6 @@ * limitations under the License. */ -import { APIRequestEvent, APIRequestFinishedEvent } from 'playwright-core/lib/server/fetch'; import { toClientCertificatesProtocol } from './browserContext'; import { ChannelOwner } from './channelOwner'; @@ -27,6 +26,7 @@ import { headersObjectToArray } from '../utils/isomorphic/headers'; import { isString } from '../utils/isomorphic/rtti'; import { Events } from './events'; +import type { APIRequestEvent, APIRequestFinishedEvent } from 'playwright-core/lib/server/fetch'; import type { Playwright } from './playwright'; import type { ClientCertificate, FilePayload, Headers, SetStorageState, StorageState } from './types'; import type { Serializable } from '../../types/structs'; diff --git a/packages/playwright-core/types/types.d.ts b/packages/playwright-core/types/types.d.ts index d5f746ad23..e516ee2b3c 100644 --- a/packages/playwright-core/types/types.d.ts +++ b/packages/playwright-core/types/types.d.ts @@ -17,6 +17,7 @@ import { ChildProcess } from 'child_process'; import { Readable } from 'stream'; import { ReadStream } from 'fs'; +import type { IncomingHttpHeaders } from 'http'; import { Protocol } from './protocol'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs'; @@ -17852,67 +17853,1135 @@ export interface APIRequestContext { * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is * read-only. */ - on(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + on(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Emitted when a request finishes in any requests created through this context. The sequence of events is * `apirequest` and `apirequestfinished` */ - on(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + on(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + once(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Adds an event listener that will be automatically removed after it is triggered once. See `addListener` for more information about this event. */ - once(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + once(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is * read-only. */ - addListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + addListener(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Emitted when a request finishes in any requests created through this context. The sequence of events is * `apirequest` and `apirequestfinished` */ - addListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + addListener(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + removeListener(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - removeListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + removeListener(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + off(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Removes an event listener added by `on` or `addListener`. */ - off(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + off(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Emitted when a request is issued from any requests created through this context. The [APIRequestEvent] object is * read-only. */ - prependListener(event: 'apirequest', listener: (aPIRequestEvent: APIRequestEvent) => any): this; + prependListener(event: 'apirequest', listener: (data: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }) => any): this; /** * Emitted when a request finishes in any requests created through this context. The sequence of events is * `apirequest` and `apirequestfinished` */ - prependListener(event: 'apirequestfinished', listener: (aPIRequestFinishedEvent: APIRequestFinishedEvent) => any): this; + prependListener(event: 'apirequestfinished', listener: (data: { + /** + * HTTP version + */ + httpVersion: string; + + /** + * raw headers + */ + rawHeaders: Array; + + /** + * status code + */ + statusCode: number; + + /** + * status message + */ + statusMessage: string; + + /** + * response body + */ + body?: Buffer; + + /** + * response headers + */ + headers: IncomingHttpHeaders; + + /** + * server IP address + */ + serverIPAddress?: string; + + /** + * server port + */ + serverPort?: number; + + timings: { + blocked?: number; + + dns?: number; + + connect?: number; + + send: number; + + wait: number; + + receive: number; + + ssl?: number; + + comment?: string; + }; + + cookies: Array<{ + name: string; + + value: string; + + domain: string; + + path: string; + + /** + * Unix time in seconds. + */ + expires: number; + + httpOnly: boolean; + + secure: boolean; + + sameSite: "Strict"|"Lax"|"None"; + }>; + + /** + * security details + */ + securityDetails?: { + /** + * Common Name component of the Issuer field. from the certificate. This should only be used for informational + * purposes. Optional. + */ + issuer?: string; + + /** + * The specific TLS protocol used. (e.g. `TLS 1.3`). Optional. + */ + protocol?: string; + + /** + * Common Name component of the Subject field from the certificate. This should only be used for informational + * purposes. Optional. + */ + subjectName?: string; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes valid. Optional. + */ + validFrom?: number; + + /** + * Unix timestamp (in seconds) specifying when this cert becomes invalid. Optional. + */ + validTo?: number; + }; + + /** + * request event object + */ + requestEvent: { + /** + * request GUID + */ + guid: string; + + /** + * request URL + */ + url: URL; + + /** + * request method + */ + method: string; + + /** + * request headers + */ + headers: { [key: string]: string; }; + + cookies: Array<{ + name: string; + + value: string; + }>; + + /** + * request post data + */ + postData?: Buffer; + }; + }) => any): this; /** * Sends HTTP(S) [DELETE](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/DELETE) request and returns its diff --git a/utils/generate_types/overrides.d.ts b/utils/generate_types/overrides.d.ts index de8c95376b..bea22eff86 100644 --- a/utils/generate_types/overrides.d.ts +++ b/utils/generate_types/overrides.d.ts @@ -16,6 +16,7 @@ import { ChildProcess } from 'child_process'; import { Readable } from 'stream'; import { ReadStream } from 'fs'; +import type { IncomingHttpHeaders } from 'http'; import { Protocol } from './protocol'; import { Serializable, EvaluationArgument, PageFunction, PageFunctionOn, SmartHandle, ElementHandleForTag, BindingSource } from './structs';