@@ -5,7 +5,7 @@ const { AbortError } = require('../core/errors')
5
5
const { extractBody, cloneBody, mixinBody } = require ( './body' )
6
6
const util = require ( '../core/util' )
7
7
const { kEnumerableProperty } = util
8
- const { responseURL, isValidReasonPhrase, toUSVString, isCancelled, isAborted } = require ( './util' )
8
+ const { responseURL, isValidReasonPhrase, toUSVString, isCancelled, isAborted, serializeJavascriptValueToJSONString } = require ( './util' )
9
9
const {
10
10
redirectStatus,
11
11
nullBodyStatus,
@@ -35,6 +35,50 @@ class Response {
35
35
return responseObject
36
36
}
37
37
38
+ // https://fetch.spec.whatwg.org/#dom-response-json
39
+ static json ( data , init = { } ) {
40
+ if ( arguments . length === 0 ) {
41
+ throw new TypeError (
42
+ 'Failed to execute \'json\' on \'Response\': 1 argument required, but 0 present.'
43
+ )
44
+ }
45
+
46
+ if ( init === null || typeof init !== 'object' ) {
47
+ throw new TypeError (
48
+ `Failed to execute 'json' on 'Response': init must be a RequestInit, found ${ typeof init } .`
49
+ )
50
+ }
51
+
52
+ init = {
53
+ status : 200 ,
54
+ statusText : '' ,
55
+ headers : new HeadersList ( ) ,
56
+ ...init
57
+ }
58
+
59
+ // 1. Let bytes the result of running serialize a JavaScript value to JSON bytes on data.
60
+ const bytes = new TextEncoder ( 'utf-8' ) . encode (
61
+ serializeJavascriptValueToJSONString ( data )
62
+ )
63
+
64
+ // 2. Let body be the result of extracting bytes.
65
+ const body = extractBody ( bytes )
66
+
67
+ // 3. Let responseObject be the result of creating a Response object, given a new response,
68
+ // "response", and this’s relevant Realm.
69
+ const relevantRealm = { settingsObject : { } }
70
+ const responseObject = new Response ( )
71
+ responseObject [ kRealm ] = relevantRealm
72
+ responseObject [ kHeaders ] [ kGuard ] = 'response'
73
+ responseObject [ kHeaders ] [ kRealm ] = relevantRealm
74
+
75
+ // 4. Perform initialize a response given responseObject, init, and (body, "application/json").
76
+ initializeResponse ( responseObject , init , { body : body [ 0 ] , type : 'application/json' } )
77
+
78
+ // 5. Return responseObject.
79
+ return responseObject
80
+ }
81
+
38
82
// Creates a redirect Response that redirects to url with status status.
39
83
static redirect ( ...args ) {
40
84
const relevantRealm = { settingsObject : { } }
@@ -105,76 +149,28 @@ class Response {
105
149
// TODO
106
150
this [ kRealm ] = { settingsObject : { } }
107
151
108
- // 1. If init["status"] is not in the range 200 to 599, inclusive, then
109
- // throw a RangeError.
110
- if ( 'status' in init && init . status !== undefined ) {
111
- if ( ! Number . isFinite ( init . status ) ) {
112
- throw new TypeError ( )
113
- }
114
-
115
- if ( init . status < 200 || init . status > 599 ) {
116
- throw new RangeError (
117
- `Failed to construct 'Response': The status provided (${ init . status } ) is outside the range [200, 599].`
118
- )
119
- }
120
- }
121
-
122
- if ( 'statusText' in init && init . statusText !== undefined ) {
123
- // 2. If init["statusText"] does not match the reason-phrase token
124
- // production, then throw a TypeError.
125
- // See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2:
126
- // reason-phrase = *( HTAB / SP / VCHAR / obs-text )
127
- if ( ! isValidReasonPhrase ( String ( init . statusText ) ) ) {
128
- throw new TypeError ( 'Invalid statusText' )
129
- }
130
- }
131
-
132
- // 3. Set this’s response to a new response.
152
+ // 1. Set this’s response to a new response.
133
153
this [ kState ] = makeResponse ( { } )
134
154
135
- // 4 . Set this’s headers to a new Headers object with this’s relevant
155
+ // 2 . Set this’s headers to a new Headers object with this’s relevant
136
156
// Realm, whose header list is this’s response’s header list and guard
137
157
// is "response".
138
158
this [ kHeaders ] = new Headers ( )
139
159
this [ kHeaders ] [ kGuard ] = 'response'
140
160
this [ kHeaders ] [ kHeadersList ] = this [ kState ] . headersList
141
161
this [ kHeaders ] [ kRealm ] = this [ kRealm ]
142
162
143
- // 5. Set this’s response’s status to init["status"].
144
- if ( 'status' in init && init . status !== undefined ) {
145
- this [ kState ] . status = init . status
146
- }
147
-
148
- // 6. Set this’s response’s status message to init["statusText"].
149
- if ( 'statusText' in init && init . statusText !== undefined ) {
150
- this [ kState ] . statusText = String ( init . statusText )
151
- }
152
-
153
- // 7. If init["headers"] exists, then fill this’s headers with init["headers"].
154
- if ( 'headers' in init ) {
155
- fill ( this [ kState ] . headersList , init . headers )
156
- }
163
+ // 3. Let bodyWithType be null.
164
+ let bodyWithType = null
157
165
158
- // 8 . If body is non-null, then:
166
+ // 4 . If body is non-null, then set bodyWithType to the result of extracting body.
159
167
if ( body != null ) {
160
- // 1. If init["status"] is a null body status, then throw a TypeError.
161
- if ( nullBodyStatus . includes ( init . status ) ) {
162
- throw new TypeError ( 'Response with null body status cannot have body' )
163
- }
164
-
165
- // 2. Let Content-Type be null.
166
- // 3. Set this’s response’s body and Content-Type to the result of
167
- // extracting body.
168
- const [ extractedBody , contentType ] = extractBody ( body )
169
- this [ kState ] . body = extractedBody
170
-
171
- // 4. If Content-Type is non-null and this’s response’s header list does
172
- // not contain `Content-Type`, then append `Content-Type`/Content-Type
173
- // to this’s response’s header list.
174
- if ( contentType && ! this . headers . has ( 'content-type' ) ) {
175
- this . headers . append ( 'content-type' , contentType )
176
- }
168
+ const [ extractedBody , type ] = extractBody ( body )
169
+ bodyWithType = { body : extractedBody , type }
177
170
}
171
+
172
+ // 5. Perform initialize a response given this, init, and bodyWithType.
173
+ initializeResponse ( this , init , bodyWithType )
178
174
}
179
175
180
176
get [ Symbol . toStringTag ] ( ) {
@@ -473,6 +469,57 @@ function makeAppropriateNetworkError (fetchParams) {
473
469
: makeNetworkError ( fetchParams . controller . terminated . reason )
474
470
}
475
471
472
+ // https://whatpr.org/fetch/1392.html#initialize-a-response
473
+ function initializeResponse ( response , init , body ) {
474
+ // 1. If init["status"] is not in the range 200 to 599, inclusive, then
475
+ // throw a RangeError.
476
+ if ( init . status != null && ( init . status < 200 || init . status > 599 ) ) {
477
+ throw new RangeError ( 'init["status"] must be in the range of 200 to 599, inclusive.' )
478
+ }
479
+
480
+ // 2. If init["statusText"] does not match the reason-phrase token production,
481
+ // then throw a TypeError.
482
+ if ( 'statusText' in init && init . statusText != null ) {
483
+ // See, https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2:
484
+ // reason-phrase = *( HTAB / SP / VCHAR / obs-text )
485
+ if ( ! isValidReasonPhrase ( String ( init . statusText ) ) ) {
486
+ throw new TypeError ( 'Invalid statusText' )
487
+ }
488
+ }
489
+
490
+ // 3. Set response’s response’s status to init["status"].
491
+ if ( 'status' in init && init . status != null ) {
492
+ response [ kState ] . status = init . status
493
+ }
494
+
495
+ // 4. Set response’s response’s status message to init["statusText"].
496
+ if ( 'statusText' in init && init . statusText != null ) {
497
+ response [ kState ] . statusText = init . statusText
498
+ }
499
+
500
+ // 5. If init["headers"] exists, then fill response’s headers with init["headers"].
501
+ if ( 'headers' in init && init . headers != null ) {
502
+ fill ( response [ kState ] . headersList , init . headers )
503
+ }
504
+
505
+ // 6. If body was given, then:
506
+ if ( body ) {
507
+ // 1. If response's status is a null body status, then throw a TypeError.
508
+ if ( nullBodyStatus . includes ( response . status ) ) {
509
+ throw new TypeError ( )
510
+ }
511
+
512
+ // 2. Set response's body to body's body.
513
+ response [ kState ] . body = body . body
514
+
515
+ // 3. If body's type is non-null and response's header list does not contain
516
+ // `Content-Type`, then append (`Content-Type`, body's type) to response's header list.
517
+ if ( body . type != null && ! response [ kState ] . headersList . has ( 'Content-Type' ) ) {
518
+ response [ kState ] . headersList . append ( 'content-type' , body . type )
519
+ }
520
+ }
521
+ }
522
+
476
523
module . exports = {
477
524
makeNetworkError,
478
525
makeResponse,
0 commit comments