@@ -15,25 +15,30 @@ export interface NetworkFormatterOptions {
1515 selectedInDevToolsUI ?: boolean ;
1616 requestIdResolver ?: ( request : HTTPRequest ) => number | string ;
1717 fetchData ?: boolean ;
18+ requestFilePath ?: string ;
19+ responseFilePath ?: string ;
20+ saveFile ?: (
21+ data : Uint8Array < ArrayBufferLike > ,
22+ filename : string ,
23+ ) => Promise < { filename : string } > ;
1824}
1925
2026export class NetworkFormatter {
2127 #request: HTTPRequest ;
2228 #options: NetworkFormatterOptions ;
2329 #requestBody?: string ;
2430 #responseBody?: string ;
31+ #requestBodyFilePath?: string ;
32+ #responseBodyFilePath?: string ;
2533
26- private constructor (
27- request : HTTPRequest ,
28- options : NetworkFormatterOptions = { } ,
29- ) {
34+ private constructor ( request : HTTPRequest , options : NetworkFormatterOptions ) {
3035 this . #request = request ;
3136 this . #options = options ;
3237 }
3338
3439 static async from (
3540 request : HTTPRequest ,
36- options : NetworkFormatterOptions = { } ,
41+ options : NetworkFormatterOptions ,
3742 ) : Promise < NetworkFormatter > {
3843 const instance = new NetworkFormatter ( request , options ) ;
3944 if ( options . fetchData ) {
@@ -47,15 +52,40 @@ export class NetworkFormatter {
4752 if ( this . #request. hasPostData ( ) ) {
4853 const data = this . #request. postData ( ) ;
4954 if ( data ) {
50- this . #requestBody = getSizeLimitedString ( data , BODY_CONTEXT_SIZE_LIMIT ) ;
55+ if ( this . #options. requestFilePath ) {
56+ if ( ! this . #options. saveFile ) {
57+ throw new Error ( 'saveFile is not provided' ) ;
58+ }
59+ await this . #options. saveFile (
60+ Buffer . from ( data ) ,
61+ this . #options. requestFilePath ,
62+ ) ;
63+ this . #requestBodyFilePath = this . #options. requestFilePath ;
64+ } else {
65+ this . #requestBody = getSizeLimitedString (
66+ data ,
67+ BODY_CONTEXT_SIZE_LIMIT ,
68+ ) ;
69+ }
5170 } else {
5271 try {
5372 const fetchData = await this . #request. fetchPostData ( ) ;
5473 if ( fetchData ) {
55- this . #requestBody = getSizeLimitedString (
56- fetchData ,
57- BODY_CONTEXT_SIZE_LIMIT ,
58- ) ;
74+ if ( this . #options. requestFilePath ) {
75+ if ( ! this . #options. saveFile ) {
76+ throw new Error ( 'saveFile is not provided' ) ;
77+ }
78+ await this . #options. saveFile (
79+ Buffer . from ( fetchData ) ,
80+ this . #options. requestFilePath ,
81+ ) ;
82+ this . #requestBodyFilePath = this . #options. requestFilePath ;
83+ } else {
84+ this . #requestBody = getSizeLimitedString (
85+ fetchData ,
86+ BODY_CONTEXT_SIZE_LIMIT ,
87+ ) ;
88+ }
5989 }
6090 } catch {
6191 this . #requestBody = '<not available anymore>' ;
@@ -66,10 +96,17 @@ export class NetworkFormatter {
6696 // Load Response Body
6797 const response = this . #request. response ( ) ;
6898 if ( response ) {
69- this . #responseBody = await this . #getFormattedResponseBody(
70- response ,
71- BODY_CONTEXT_SIZE_LIMIT ,
72- ) ;
99+ if ( this . #options. responseFilePath ) {
100+ this . #responseBodyFilePath = await this . #saveResponseBodyToFile(
101+ response ,
102+ this . #options. responseFilePath ,
103+ ) ;
104+ } else {
105+ this . #responseBody = await this . #getFormattedResponseBody(
106+ response ,
107+ BODY_CONTEXT_SIZE_LIMIT ,
108+ ) ;
109+ }
73110 }
74111 }
75112
@@ -90,6 +127,9 @@ export class NetworkFormatter {
90127 if ( this . #requestBody) {
91128 response . push ( `### Request Body` ) ;
92129 response . push ( this . #requestBody) ;
130+ } else if ( this . #requestBodyFilePath) {
131+ response . push ( `### Request Body` ) ;
132+ response . push ( `Saved to ${ this . #requestBodyFilePath} .` ) ;
93133 }
94134
95135 const httpResponse = this . #request. response ( ) ;
@@ -105,6 +145,9 @@ export class NetworkFormatter {
105145 if ( this . #responseBody) {
106146 response . push ( `### Response Body` ) ;
107147 response . push ( this . #responseBody) ;
148+ } else if ( this . #responseBodyFilePath) {
149+ response . push ( `### Response Body` ) ;
150+ response . push ( `Saved to ${ this . #responseBodyFilePath} .` ) ;
108151 }
109152
110153 const httpFailure = this . #request. failure ( ) ;
@@ -124,6 +167,7 @@ export class NetworkFormatter {
124167 // We create a temporary synchronous instance just for toString
125168 const formatter = new NetworkFormatter ( request , {
126169 requestId : id ,
170+ saveFile : this . #options. saveFile ,
127171 } ) ;
128172 response . push ( `${ ' ' . repeat ( indent ) } ${ formatter . toString ( ) } ` ) ;
129173 indent ++ ;
@@ -150,6 +194,7 @@ export class NetworkFormatter {
150194 : undefined ;
151195 const formatter = new NetworkFormatter ( request , {
152196 requestId : id ,
197+ saveFile : this . #options. saveFile ,
153198 } ) ;
154199 return formatter . toJSON ( ) ;
155200 } ) ;
@@ -158,8 +203,10 @@ export class NetworkFormatter {
158203 ...this . toJSON ( ) ,
159204 requestHeaders : this . #request. headers ( ) ,
160205 requestBody : this . #requestBody,
206+ requestBodyFilePath : this . #requestBodyFilePath,
161207 responseHeaders : this . #request. response ( ) ?. headers ( ) ,
162208 responseBody : this . #responseBody,
209+ responseBodyFilePath : this . #responseBodyFilePath,
163210 failure : this . #request. failure ( ) ?. errorText ,
164211 redirectChain : formattedRedirectChain . length
165212 ? formattedRedirectChain
@@ -215,6 +262,22 @@ export class NetworkFormatter {
215262 return '<not available anymore>' ;
216263 }
217264 }
265+
266+ async #saveResponseBodyToFile(
267+ httpResponse : HTTPResponse ,
268+ filePath : string ,
269+ ) : Promise < string > {
270+ try {
271+ const responseBuffer = await httpResponse . buffer ( ) ;
272+ if ( ! this . #options. saveFile ) {
273+ throw new Error ( 'saveFile is not provided' ) ;
274+ }
275+ await this . #options. saveFile ( responseBuffer , filePath ) ;
276+ return filePath ;
277+ } catch {
278+ return '<not available anymore>' ;
279+ }
280+ }
218281}
219282
220283function getSizeLimitedString ( text : string , sizeLimit : number ) {
0 commit comments