1
1
import { isPlainObject } from "./is-plain-object.js" ;
2
2
import { RequestError } from "@octokit/request-error" ;
3
- import type { EndpointInterface } from "@octokit/types" ;
3
+ import type { EndpointInterface , OctokitResponse } from "@octokit/types" ;
4
4
5
- export default function fetchWrapper (
5
+ export default async function fetchWrapper (
6
6
requestOptions : ReturnType < EndpointInterface > ,
7
- ) {
7
+ ) : Promise < OctokitResponse < any > > {
8
8
const fetch : typeof globalThis . fetch =
9
9
requestOptions . request ?. fetch || globalThis . fetch ;
10
10
@@ -30,109 +30,30 @@ export default function fetchWrapper(
30
30
] ) ,
31
31
) ;
32
32
33
- let responseHeaders : { [ header : string ] : string } = { } ;
34
- let status : number ;
35
- let url : string ;
36
-
37
- return fetch ( requestOptions . url , {
38
- method : requestOptions . method ,
39
- body,
40
- redirect : requestOptions . request ?. redirect ,
41
- // Header values must be `string`
42
- headers : requestHeaders ,
43
- signal : requestOptions . request ?. signal ,
44
- // duplex must be set if request.body is ReadableStream or Async Iterables.
45
- // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex.
46
- ...( requestOptions . body && { duplex : "half" } ) ,
47
- } )
48
- . then ( async ( response ) => {
49
- url = response . url ;
50
- status = response . status ;
51
-
52
- for ( const keyAndValue of response . headers ) {
53
- responseHeaders [ keyAndValue [ 0 ] ] = keyAndValue [ 1 ] ;
54
- }
55
-
56
- if ( "deprecation" in responseHeaders ) {
57
- const matches =
58
- responseHeaders . link &&
59
- responseHeaders . link . match ( / < ( [ ^ > ] + ) > ; r e l = " d e p r e c a t i o n " / ) ;
60
- const deprecationLink = matches && matches . pop ( ) ;
61
- log . warn (
62
- `[@octokit/request] "${ requestOptions . method } ${
63
- requestOptions . url
64
- } " is deprecated. It is scheduled to be removed on ${ responseHeaders . sunset } ${
65
- deprecationLink ? `. See ${ deprecationLink } ` : ""
66
- } `,
67
- ) ;
68
- }
69
-
70
- if ( status === 204 || status === 205 ) {
71
- return ;
72
- }
73
-
74
- // GitHub API returns 200 for HEAD requests
75
- if ( requestOptions . method === "HEAD" ) {
76
- if ( status < 400 ) {
77
- return ;
78
- }
79
-
80
- throw new RequestError ( response . statusText , status , {
81
- response : {
82
- url,
83
- status,
84
- headers : responseHeaders ,
85
- data : undefined ,
86
- } ,
87
- request : requestOptions ,
88
- } ) ;
89
- }
90
-
91
- if ( status === 304 ) {
92
- throw new RequestError ( "Not modified" , status , {
93
- response : {
94
- url,
95
- status,
96
- headers : responseHeaders ,
97
- data : await getResponseData ( response ) ,
98
- } ,
99
- request : requestOptions ,
100
- } ) ;
101
- }
102
-
103
- if ( status >= 400 ) {
104
- const data = await getResponseData ( response ) ;
105
-
106
- const error = new RequestError ( toErrorMessage ( data ) , status , {
107
- response : {
108
- url,
109
- status,
110
- headers : responseHeaders ,
111
- data,
112
- } ,
113
- request : requestOptions ,
114
- } ) ;
115
-
33
+ let fetchResponse : Response ;
34
+
35
+ try {
36
+ fetchResponse = await fetch ( requestOptions . url , {
37
+ method : requestOptions . method ,
38
+ body,
39
+ redirect : requestOptions . request ?. redirect ,
40
+ // Header values must be `string`
41
+ headers : requestHeaders ,
42
+ signal : requestOptions . request ?. signal ,
43
+ // duplex must be set if request.body is ReadableStream or Async Iterables.
44
+ // See https://fetch.spec.whatwg.org/#dom-requestinit-duplex.
45
+ ...( requestOptions . body && { duplex : "half" } ) ,
46
+ } ) ;
47
+ // wrap fetch errors as RequestError if it is not a AbortError
48
+ } catch ( error ) {
49
+ let message = "Unknown Error" ;
50
+ if ( error instanceof Error ) {
51
+ if ( error . name === "AbortError" ) {
52
+ ( error as RequestError ) . status = 500 ;
116
53
throw error ;
117
54
}
118
55
119
- return parseSuccessResponseBody
120
- ? await getResponseData ( response )
121
- : response . body ;
122
- } )
123
- . then ( ( data ) => {
124
- return {
125
- status,
126
- url,
127
- headers : responseHeaders ,
128
- data,
129
- } ;
130
- } )
131
- . catch ( ( error ) => {
132
- if ( error instanceof RequestError ) throw error ;
133
- else if ( error . name === "AbortError" ) throw error ;
134
-
135
- let message = error . message ;
56
+ message = error . message ;
136
57
137
58
// undici throws a TypeError for network errors
138
59
// and puts the error message in `error.cause`
@@ -144,11 +65,95 @@ export default function fetchWrapper(
144
65
message = error . cause ;
145
66
}
146
67
}
68
+ }
147
69
148
- throw new RequestError ( message , 500 , {
149
- request : requestOptions ,
150
- } ) ;
70
+ const requestError = new RequestError ( message , 500 , {
71
+ request : requestOptions ,
72
+ } ) ;
73
+ requestError . cause = error ;
74
+
75
+ throw requestError ;
76
+ }
77
+
78
+ const status = fetchResponse . status ;
79
+ const url = fetchResponse . url ;
80
+ const responseHeaders : { [ header : string ] : string } = { } ;
81
+
82
+ for ( const keyAndValue of fetchResponse . headers ) {
83
+ responseHeaders [ keyAndValue [ 0 ] ] = keyAndValue [ 1 ] ;
84
+ }
85
+
86
+ const octokitResponse : OctokitResponse < any > = {
87
+ url,
88
+ status,
89
+ headers : responseHeaders ,
90
+ data : "" ,
91
+ } ;
92
+
93
+ if ( "deprecation" in responseHeaders ) {
94
+ const matches =
95
+ responseHeaders . link &&
96
+ responseHeaders . link . match ( / < ( [ ^ > ] + ) > ; r e l = " d e p r e c a t i o n " / ) ;
97
+ const deprecationLink = matches && matches . pop ( ) ;
98
+ log . warn (
99
+ `[@octokit/request] "${ requestOptions . method } ${
100
+ requestOptions . url
101
+ } " is deprecated. It is scheduled to be removed on ${ responseHeaders . sunset } ${
102
+ deprecationLink ? `. See ${ deprecationLink } ` : ""
103
+ } `,
104
+ ) ;
105
+ }
106
+
107
+ if ( status === 204 || status === 205 ) {
108
+ return octokitResponse ;
109
+ }
110
+
111
+ // GitHub API returns 200 for HEAD requests
112
+ if ( requestOptions . method === "HEAD" ) {
113
+ if ( status < 400 ) {
114
+ return octokitResponse ;
115
+ }
116
+
117
+ throw new RequestError ( fetchResponse . statusText , status , {
118
+ response : octokitResponse ,
119
+ request : requestOptions ,
120
+ } ) ;
121
+ }
122
+
123
+ if ( status === 304 ) {
124
+ octokitResponse . data = await getResponseData ( fetchResponse ) ;
125
+
126
+ throw new RequestError ( "Not modified" , status , {
127
+ response : octokitResponse ,
128
+ request : requestOptions ,
151
129
} ) ;
130
+ }
131
+
132
+ if ( status >= 400 ) {
133
+ octokitResponse . data = await getResponseData ( fetchResponse ) ;
134
+
135
+ const error = new RequestError (
136
+ toErrorMessage ( octokitResponse . data ) ,
137
+ status ,
138
+ {
139
+ response : octokitResponse ,
140
+ request : requestOptions ,
141
+ } ,
142
+ ) ;
143
+
144
+ throw error ;
145
+ }
146
+
147
+ const responseBody = parseSuccessResponseBody
148
+ ? await getResponseData ( fetchResponse )
149
+ : fetchResponse . body ;
150
+
151
+ return {
152
+ status,
153
+ url,
154
+ headers : responseHeaders ,
155
+ data : responseBody ,
156
+ } ;
152
157
}
153
158
154
159
async function getResponseData ( response : Response ) {
@@ -167,7 +172,10 @@ async function getResponseData(response: Response) {
167
172
) ;
168
173
}
169
174
170
- if ( ! contentType || / ^ t e x t \/ | c h a r s e t = u t f - 8 $ / . test ( contentType ) ) {
175
+ if (
176
+ ( ! contentType || / ^ t e x t \/ | c h a r s e t = u t f - 8 $ / . test ( contentType ) ) &&
177
+ response . text
178
+ ) {
171
179
return response . text ( ) ;
172
180
}
173
181
0 commit comments