undici 5.28.2 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -126,7 +126,13 @@ function URLSerializer (url, excludeFragment = false) {
126
126
  const href = url.href
127
127
  const hashLength = url.hash.length
128
128
 
129
- return hashLength === 0 ? href : href.substring(0, href.length - hashLength)
129
+ const serialized = hashLength === 0 ? href : href.substring(0, href.length - hashLength)
130
+
131
+ if (!hashLength && href.endsWith('#')) {
132
+ return serialized.slice(0, -1)
133
+ }
134
+
135
+ return serialized
130
136
  }
131
137
 
132
138
  // https://infra.spec.whatwg.org/#collect-a-sequence-of-code-points
@@ -543,7 +549,7 @@ function serializeAMimeType (mimeType) {
543
549
  // 4. If value does not solely contain HTTP token code
544
550
  // points or value is the empty string, then:
545
551
  if (!HTTP_TOKEN_CODEPOINTS.test(value)) {
546
- // 1. Precede each occurence of U+0022 (") or
552
+ // 1. Precede each occurrence of U+0022 (") or
547
553
  // U+005C (\) in value with U+005C (\).
548
554
  value = value.replace(/(\\|")/g, '\\$1')
549
555
 
@@ -40,25 +40,25 @@ const {
40
40
  isomorphicEncode,
41
41
  urlIsLocal,
42
42
  urlIsHttpHttpsScheme,
43
- urlHasHttpsScheme
43
+ urlHasHttpsScheme,
44
+ simpleRangeHeaderValue,
45
+ buildContentRange
44
46
  } = require('./util')
45
47
  const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
46
48
  const assert = require('assert')
47
- const { safelyExtractBody } = require('./body')
49
+ const { safelyExtractBody, extractBody } = require('./body')
48
50
  const {
49
51
  redirectStatusSet,
50
52
  nullBodyStatus,
51
53
  safeMethodsSet,
52
54
  requestBodyHeader,
53
- subresourceSet,
54
- DOMException
55
+ subresourceSet
55
56
  } = require('./constants')
56
- const { kHeadersList } = require('../core/symbols')
57
+ const { kHeadersList, kConstruct } = require('../core/symbols')
57
58
  const EE = require('events')
58
59
  const { Readable, pipeline } = require('stream')
59
60
  const { addAbortListener, isErrored, isReadable, nodeMajor, nodeMinor } = require('../core/util')
60
- const { dataURLProcessor, serializeAMimeType } = require('./dataURL')
61
- const { TransformStream } = require('stream/web')
61
+ const { dataURLProcessor, serializeAMimeType, parseMIMEType } = require('./dataURL')
62
62
  const { getGlobalDispatcher } = require('../global')
63
63
  const { webidl } = require('./webidl')
64
64
  const { STATUS_CODES } = require('http')
@@ -66,7 +66,6 @@ const GET_OR_HEAD = ['GET', 'HEAD']
66
66
 
67
67
  /** @type {import('buffer').resolveObjectURL} */
68
68
  let resolveObjectURL
69
- let ReadableStream = globalThis.ReadableStream
70
69
 
71
70
  class Fetch extends EE {
72
71
  constructor (dispatcher) {
@@ -232,9 +231,10 @@ function fetch (input, init = {}) {
232
231
 
233
232
  // 4. Set responseObject to the result of creating a Response object,
234
233
  // given response, "immutable", and relevantRealm.
235
- responseObject = new Response()
234
+ responseObject = new Response(kConstruct)
236
235
  responseObject[kState] = response
237
236
  responseObject[kRealm] = relevantRealm
237
+ responseObject[kHeaders] = new Headers(kConstruct)
238
238
  responseObject[kHeaders][kHeadersList] = response.headersList
239
239
  responseObject[kHeaders][kGuard] = 'immutable'
240
240
  responseObject[kHeaders][kRealm] = relevantRealm
@@ -404,9 +404,9 @@ function fetching ({
404
404
  // 5. Let timingInfo be a new fetch timing info whose start time and
405
405
  // post-redirect start time are the coarsened shared current time given
406
406
  // crossOriginIsolatedCapability.
407
- const currenTime = coarsenedSharedCurrentTime(crossOriginIsolatedCapability)
407
+ const currentTime = coarsenedSharedCurrentTime(crossOriginIsolatedCapability)
408
408
  const timingInfo = createOpaqueTimingInfo({
409
- startTime: currenTime
409
+ startTime: currentTime
410
410
  })
411
411
 
412
412
  // 6. Let fetchParams be a new fetch params whose
@@ -815,38 +815,118 @@ function schemeFetch (fetchParams) {
815
815
  return Promise.resolve(makeNetworkError('NetworkError when attempting to fetch resource.'))
816
816
  }
817
817
 
818
- const blobURLEntryObject = resolveObjectURL(blobURLEntry.toString())
818
+ const blob = resolveObjectURL(blobURLEntry.toString())
819
819
 
820
820
  // 2. If request’s method is not `GET`, blobURLEntry is null, or blobURLEntry’s
821
821
  // object is not a Blob object, then return a network error.
822
- if (request.method !== 'GET' || !isBlobLike(blobURLEntryObject)) {
822
+ if (request.method !== 'GET' || !isBlobLike(blob)) {
823
823
  return Promise.resolve(makeNetworkError('invalid method'))
824
824
  }
825
825
 
826
- // 3. Let bodyWithType be the result of safely extracting blobURLEntry’s object.
827
- const bodyWithType = safelyExtractBody(blobURLEntryObject)
826
+ // 3. Let blob be blobURLEntry’s object.
827
+ // Note: done above
828
828
 
829
- // 4. Let body be bodyWithType’s body.
830
- const body = bodyWithType[0]
829
+ // 4. Let response be a new response.
830
+ const response = makeResponse()
831
831
 
832
- // 5. Let length be body’s length, serialized and isomorphic encoded.
833
- const length = isomorphicEncode(`${body.length}`)
832
+ // 5. Let fullLength be blob’s size.
833
+ const fullLength = blob.size
834
834
 
835
- // 6. Let type be bodyWithType’s type if it is non-null; otherwise the empty byte sequence.
836
- const type = bodyWithType[1] ?? ''
835
+ // 6. Let serializedFullLength be fullLength, serialized and isomorphic encoded.
836
+ const serializedFullLength = isomorphicEncode(`${fullLength}`)
837
837
 
838
- // 7. Return a new response whose status message is `OK`, header list is
839
- // « (`Content-Length`, length), (`Content-Type`, type) », and body is body.
840
- const response = makeResponse({
841
- statusText: 'OK',
842
- headersList: [
843
- ['content-length', { name: 'Content-Length', value: length }],
844
- ['content-type', { name: 'Content-Type', value: type }]
845
- ]
846
- })
838
+ // 7. Let type be blob’s type.
839
+ const type = blob.type
840
+
841
+ // 8. If request’s header list does not contain `Range`:
842
+ // 9. Otherwise:
843
+ if (!request.headersList.contains('range')) {
844
+ // 1. Let bodyWithType be the result of safely extracting blob.
845
+ // Note: in the FileAPI a blob "object" is a Blob *or* a MediaSource.
846
+ // In node, this can only ever be a Blob. Therefore we can safely
847
+ // use extractBody directly.
848
+ const bodyWithType = extractBody(blob)
849
+
850
+ // 2. Set response’s status message to `OK`.
851
+ response.statusText = 'OK'
852
+
853
+ // 3. Set response’s body to bodyWithType’s body.
854
+ response.body = bodyWithType[0]
855
+
856
+ // 4. Set response’s header list to « (`Content-Length`, serializedFullLength), (`Content-Type`, type) ».
857
+ response.headersList.set('content-length', serializedFullLength)
858
+ response.headersList.set('content-type', type)
859
+ } else {
860
+ // 1. Set response’s range-requested flag.
861
+ response.rangeRequested = true
862
+
863
+ // 2. Let rangeHeader be the result of getting `Range` from request’s header list.
864
+ const rangeHeader = request.headersList.get('range')
865
+
866
+ // 3. Let rangeValue be the result of parsing a single range header value given rangeHeader and true.
867
+ const rangeValue = simpleRangeHeaderValue(rangeHeader, true)
868
+
869
+ // 4. If rangeValue is failure, then return a network error.
870
+ if (rangeValue === 'failure') {
871
+ return Promise.resolve(makeNetworkError('failed to fetch the data URL'))
872
+ }
873
+
874
+ // 5. Let (rangeStart, rangeEnd) be rangeValue.
875
+ let { rangeStartValue: rangeStart, rangeEndValue: rangeEnd } = rangeValue
876
+
877
+ // 6. If rangeStart is null:
878
+ // 7. Otherwise:
879
+ if (rangeStart === null) {
880
+ // 1. Set rangeStart to fullLength − rangeEnd.
881
+ rangeStart = fullLength - rangeEnd
882
+
883
+ // 2. Set rangeEnd to rangeStart + rangeEnd − 1.
884
+ rangeEnd = rangeStart + rangeEnd - 1
885
+ } else {
886
+ // 1. If rangeStart is greater than or equal to fullLength, then return a network error.
887
+ if (rangeStart >= fullLength) {
888
+ return Promise.resolve(makeNetworkError('Range start is greater than the blob\'s size.'))
889
+ }
890
+
891
+ // 2. If rangeEnd is null or rangeEnd is greater than or equal to fullLength, then set
892
+ // rangeEnd to fullLength − 1.
893
+ if (rangeEnd === null || rangeEnd >= fullLength) {
894
+ rangeEnd = fullLength - 1
895
+ }
896
+ }
897
+
898
+ // 8. Let slicedBlob be the result of invoking slice blob given blob, rangeStart,
899
+ // rangeEnd + 1, and type.
900
+ const slicedBlob = blob.slice(rangeStart, rangeEnd, type)
901
+
902
+ // 9. Let slicedBodyWithType be the result of safely extracting slicedBlob.
903
+ // Note: same reason as mentioned above as to why we use extractBody
904
+ const slicedBodyWithType = extractBody(slicedBlob)
847
905
 
848
- response.body = body
906
+ // 10. Set response’s body to slicedBodyWithType’s body.
907
+ response.body = slicedBodyWithType[0]
849
908
 
909
+ // 11. Let serializedSlicedLength be slicedBlob’s size, serialized and isomorphic encoded.
910
+ const serializedSlicedLength = isomorphicEncode(`${slicedBlob.size}`)
911
+
912
+ // 12. Let contentRange be the result of invoking build a content range given rangeStart,
913
+ // rangeEnd, and fullLength.
914
+ const contentRange = buildContentRange(rangeStart, rangeEnd, fullLength)
915
+
916
+ // 13. Set response’s status to 206.
917
+ response.status = 206
918
+
919
+ // 14. Set response’s status message to `Partial Content`.
920
+ response.statusText = 'Partial Content'
921
+
922
+ // 15. Set response’s header list to « (`Content-Length`, serializedSlicedLength),
923
+ // (`Content-Type`, type), (`Content-Range`, contentRange) ».
924
+ response.headersList.set('content-length', serializedSlicedLength)
925
+ response.headersList.set('content-type', type)
926
+ response.headersList.set('content-range', contentRange)
927
+ }
928
+
929
+ // 10. Return response.
850
930
  return Promise.resolve(response)
851
931
  }
852
932
  case 'data:': {
@@ -908,92 +988,147 @@ function finalizeResponse (fetchParams, response) {
908
988
 
909
989
  // https://fetch.spec.whatwg.org/#fetch-finale
910
990
  function fetchFinale (fetchParams, response) {
911
- // 1. If response is a network error, then:
912
- if (response.type === 'error') {
913
- // 1. Set response’s URL list to « fetchParams’s request’s URL list[0] ».
914
- response.urlList = [fetchParams.request.urlList[0]]
915
-
916
- // 2. Set response’s timing info to the result of creating an opaque timing
917
- // info for fetchParams’s timing info.
918
- response.timingInfo = createOpaqueTimingInfo({
919
- startTime: fetchParams.timingInfo.startTime
920
- })
921
- }
991
+ // 1. Let timingInfo be fetchParams’s timing info.
992
+ let timingInfo = fetchParams.timingInfo
993
+
994
+ // 2. If response is not a network error and fetchParams’s request’s client is a secure context,
995
+ // then set timingInfo’s server-timing headers to the result of getting, decoding, and splitting
996
+ // `Server-Timing` from response’s internal response’s header list.
997
+ // TODO
922
998
 
923
- // 2. Let processResponseEndOfBody be the following steps:
999
+ // 3. Let processResponseEndOfBody be the following steps:
924
1000
  const processResponseEndOfBody = () => {
925
- // 1. Set fetchParams’s request’s done flag.
926
- fetchParams.request.done = true
927
-
928
- // If fetchParams’s process response end-of-body is not null,
929
- // then queue a fetch task to run fetchParams’s process response
930
- // end-of-body given response with fetchParams’s task destination.
931
- if (fetchParams.processResponseEndOfBody != null) {
932
- queueMicrotask(() => fetchParams.processResponseEndOfBody(response))
1001
+ // 1. Let unsafeEndTime be the unsafe shared current time.
1002
+ const unsafeEndTime = Date.now() // ?
1003
+
1004
+ // 2. If fetchParams’s request’s destination is "document", then set fetchParams’s controller’s
1005
+ // full timing info to fetchParams’s timing info.
1006
+ if (fetchParams.request.destination === 'document') {
1007
+ fetchParams.controller.fullTimingInfo = timingInfo
1008
+ }
1009
+
1010
+ // 3. Set fetchParams’s controller’s report timing steps to the following steps given a global object global:
1011
+ fetchParams.controller.reportTimingSteps = () => {
1012
+ // 1. If fetchParams’s request’s URL’s scheme is not an HTTP(S) scheme, then return.
1013
+ if (fetchParams.request.url.protocol !== 'https:') {
1014
+ return
1015
+ }
1016
+
1017
+ // 2. Set timingInfo’s end time to the relative high resolution time given unsafeEndTime and global.
1018
+ timingInfo.endTime = unsafeEndTime
1019
+
1020
+ // 3. Let cacheState be response’s cache state.
1021
+ let cacheState = response.cacheState
1022
+
1023
+ // 4. Let bodyInfo be response’s body info.
1024
+ const bodyInfo = response.bodyInfo
1025
+
1026
+ // 5. If response’s timing allow passed flag is not set, then set timingInfo to the result of creating an
1027
+ // opaque timing info for timingInfo and set cacheState to the empty string.
1028
+ if (!response.timingAllowPassed) {
1029
+ timingInfo = createOpaqueTimingInfo(timingInfo)
1030
+
1031
+ cacheState = ''
1032
+ }
1033
+
1034
+ // 6. Let responseStatus be 0.
1035
+ let responseStatus = 0
1036
+
1037
+ // 7. If fetchParams’s request’s mode is not "navigate" or response’s has-cross-origin-redirects is false:
1038
+ if (fetchParams.request.mode !== 'navigator' || !response.hasCrossOriginRedirects) {
1039
+ // 1. Set responseStatus to response’s status.
1040
+ responseStatus = response.status
1041
+
1042
+ // 2. Let mimeType be the result of extracting a MIME type from response’s header list.
1043
+ const mimeType = parseMIMEType(response.headersList.get('content-type')) // TODO: fix
1044
+
1045
+ // 3. If mimeType is not failure, then set bodyInfo’s content type to the result of minimizing a supported MIME type given mimeType.
1046
+ if (mimeType !== 'failure') {
1047
+ // TODO
1048
+ }
1049
+ }
1050
+
1051
+ // 8. If fetchParams’s request’s initiator type is non-null, then mark resource timing given timingInfo,
1052
+ // fetchParams’s request’s URL, fetchParams’s request’s initiator type, global, cacheState, bodyInfo,
1053
+ // and responseStatus.
1054
+ if (fetchParams.request.initiatorType != null) {
1055
+ // TODO: update markresourcetiming
1056
+ markResourceTiming(timingInfo, fetchParams.request.url, fetchParams.request.initiatorType, globalThis, cacheState, bodyInfo, responseStatus)
1057
+ }
933
1058
  }
1059
+
1060
+ // 4. Let processResponseEndOfBodyTask be the following steps:
1061
+ const processResponseEndOfBodyTask = () => {
1062
+ // 1. Set fetchParams’s request’s done flag.
1063
+ fetchParams.request.done = true
1064
+
1065
+ // 2. If fetchParams’s process response end-of-body is non-null, then run fetchParams’s process
1066
+ // response end-of-body given response.
1067
+ if (fetchParams.processResponseEndOfBody != null) {
1068
+ queueMicrotask(() => fetchParams.processResponseEndOfBody(response))
1069
+ }
1070
+
1071
+ // 3. If fetchParams’s request’s initiator type is non-null and fetchParams’s request’s client’s
1072
+ // global object is fetchParams’s task destination, then run fetchParams’s controller’s report
1073
+ // timing steps given fetchParams’s request’s client’s global object.
1074
+ if (fetchParams.request.initiatorType != null) {
1075
+ fetchParams.controller.reportTimingSteps()
1076
+ }
1077
+ }
1078
+
1079
+ // 5. Queue a fetch task to run processResponseEndOfBodyTask with fetchParams’s task destination
1080
+ queueMicrotask(() => processResponseEndOfBodyTask())
934
1081
  }
935
1082
 
936
- // 3. If fetchParams’s process response is non-null, then queue a fetch task
937
- // to run fetchParams’s process response given response, with fetchParams’s
938
- // task destination.
1083
+ // 4. If fetchParams’s process response is non-null, then queue a fetch task to run fetchParams’s
1084
+ // process response given response, with fetchParams’s task destination.
939
1085
  if (fetchParams.processResponse != null) {
940
1086
  queueMicrotask(() => fetchParams.processResponse(response))
941
1087
  }
942
1088
 
943
- // 4. If response’s body is null, then run processResponseEndOfBody.
944
- if (response.body == null) {
1089
+ // 5. Let internalResponse be response, if response is a network error; otherwise response’s internal response.
1090
+ const internalResponse = response.type === 'error' ? response : (response.internalResponse ?? response)
1091
+
1092
+ // 6. If internalResponse’s body is null, then run processResponseEndOfBody.
1093
+ // 7. Otherwise:
1094
+ if (internalResponse.body == null) {
945
1095
  processResponseEndOfBody()
946
1096
  } else {
947
- // 5. Otherwise:
948
-
949
- // 1. Let transformStream be a new a TransformStream.
950
-
951
- // 2. Let identityTransformAlgorithm be an algorithm which, given chunk,
952
- // enqueues chunk in transformStream.
953
- const identityTransformAlgorithm = (chunk, controller) => {
954
- controller.enqueue(chunk)
955
- }
956
-
957
- // 3. Set up transformStream with transformAlgorithm set to identityTransformAlgorithm
958
- // and flushAlgorithm set to processResponseEndOfBody.
1097
+ // 1. Let transformStream be a new TransformStream.
1098
+ // 2. Let identityTransformAlgorithm be an algorithm which, given chunk, enqueues chunk in transformStream.
1099
+ // 3. Set up transformStream with transformAlgorithm set to identityTransformAlgorithm and flushAlgorithm
1100
+ // set to processResponseEndOfBody.
959
1101
  const transformStream = new TransformStream({
960
1102
  start () {},
961
- transform: identityTransformAlgorithm,
1103
+ transform (chunk, controller) {
1104
+ controller.enqueue(chunk)
1105
+ },
962
1106
  flush: processResponseEndOfBody
963
- }, {
964
- size () {
965
- return 1
966
- }
967
- }, {
968
- size () {
969
- return 1
970
- }
971
1107
  })
972
1108
 
973
- // 4. Set response’s body to the result of piping response’s body through transformStream.
974
- response.body = { stream: response.body.stream.pipeThrough(transformStream) }
975
- }
1109
+ // 4. Set internalResponse’s body’s stream to the result of internalResponse’s body’s stream piped through transformStream.
1110
+ internalResponse.body.stream.pipeThrough(transformStream)
1111
+
1112
+ const byteStream = new ReadableStream({
1113
+ readableStream: transformStream.readable,
1114
+ async start (controller) {
1115
+ const reader = this.readableStream.getReader()
976
1116
 
977
- // 6. If fetchParams’s process response consume body is non-null, then:
978
- if (fetchParams.processResponseConsumeBody != null) {
979
- // 1. Let processBody given nullOrBytes be this step: run fetchParams’s
980
- // process response consume body given response and nullOrBytes.
981
- const processBody = (nullOrBytes) => fetchParams.processResponseConsumeBody(response, nullOrBytes)
1117
+ while (true) {
1118
+ const { done, value } = await reader.read()
982
1119
 
983
- // 2. Let processBodyError be this step: run fetchParams’s process
984
- // response consume body given response and failure.
985
- const processBodyError = (failure) => fetchParams.processResponseConsumeBody(response, failure)
1120
+ if (done) {
1121
+ queueMicrotask(() => readableStreamClose(controller))
1122
+ break
1123
+ }
986
1124
 
987
- // 3. If response’s body is null, then queue a fetch task to run processBody
988
- // given null, with fetchParams’s task destination.
989
- if (response.body == null) {
990
- queueMicrotask(() => processBody(null))
991
- } else {
992
- // 4. Otherwise, fully read response’s body given processBody, processBodyError,
993
- // and fetchParams’s task destination.
994
- return fullyReadBody(response.body, processBody, processBodyError)
995
- }
996
- return Promise.resolve()
1125
+ controller.enqueue(value)
1126
+ }
1127
+ },
1128
+ type: 'bytes'
1129
+ })
1130
+
1131
+ internalResponse.body.stream = byteStream
997
1132
  }
998
1133
  }
999
1134
 
@@ -1795,13 +1930,8 @@ async function httpNetworkFetch (
1795
1930
  // TODO
1796
1931
 
1797
1932
  // 15. Let stream be a new ReadableStream.
1798
- // 16. Set up stream with pullAlgorithm set to pullAlgorithm,
1799
- // cancelAlgorithm set to cancelAlgorithm, highWaterMark set to
1800
- // highWaterMark, and sizeAlgorithm set to sizeAlgorithm.
1801
- if (!ReadableStream) {
1802
- ReadableStream = require('stream/web').ReadableStream
1803
- }
1804
-
1933
+ // 16. Set up stream with byte reading support with pullAlgorithm set to pullAlgorithm,
1934
+ // cancelAlgorithm set to cancelAlgorithm.
1805
1935
  const stream = new ReadableStream(
1806
1936
  {
1807
1937
  async start (controller) {
@@ -1812,13 +1942,8 @@ async function httpNetworkFetch (
1812
1942
  },
1813
1943
  async cancel (reason) {
1814
1944
  await cancelAlgorithm(reason)
1815
- }
1816
- },
1817
- {
1818
- highWaterMark: 0,
1819
- size () {
1820
- return 1
1821
- }
1945
+ },
1946
+ type: 'bytes'
1822
1947
  }
1823
1948
  )
1824
1949
 
@@ -1898,7 +2023,10 @@ async function httpNetworkFetch (
1898
2023
 
1899
2024
  // 7. Enqueue a Uint8Array wrapping an ArrayBuffer containing bytes
1900
2025
  // into stream.
1901
- fetchParams.controller.controller.enqueue(new Uint8Array(bytes))
2026
+ const buffer = new Uint8Array(bytes)
2027
+ if (buffer.byteLength) {
2028
+ fetchParams.controller.controller.enqueue(buffer)
2029
+ }
1902
2030
 
1903
2031
  // 8. If stream is errored, then terminate the ongoing fetch.
1904
2032
  if (isErrored(stream)) {
@@ -32,8 +32,6 @@ const { kHeadersList, kConstruct } = require('../core/symbols')
32
32
  const assert = require('assert')
33
33
  const { getMaxListeners, setMaxListeners, getEventListeners, defaultMaxListeners } = require('events')
34
34
 
35
- let TransformStream = globalThis.TransformStream
36
-
37
35
  const kAbortController = Symbol('abortController')
38
36
 
39
37
  const requestFinalizer = new FinalizationRegistry(({ signal, abort }) => {
@@ -516,10 +514,6 @@ class Request {
516
514
  }
517
515
 
518
516
  // 2. Set finalBody to the result of creating a proxy for inputBody.
519
- if (!TransformStream) {
520
- TransformStream = require('stream/web').TransformStream
521
- }
522
-
523
517
  // https://streams.spec.whatwg.org/#readablestream-create-a-proxy
524
518
  const identityTransform = new TransformStream()
525
519
  inputBody.stream.pipeThrough(identityTransform)
@@ -15,8 +15,7 @@ const {
15
15
  } = require('./util')
16
16
  const {
17
17
  redirectStatusSet,
18
- nullBodyStatus,
19
- DOMException
18
+ nullBodyStatus
20
19
  } = require('./constants')
21
20
  const { kState, kHeaders, kGuard, kRealm } = require('./symbols')
22
21
  const { webidl } = require('./webidl')
@@ -27,7 +26,6 @@ const { kHeadersList, kConstruct } = require('../core/symbols')
27
26
  const assert = require('assert')
28
27
  const { types } = require('util')
29
28
 
30
- const ReadableStream = globalThis.ReadableStream || require('stream/web').ReadableStream
31
29
  const textEncoder = new TextEncoder('utf-8')
32
30
 
33
31
  // https://fetch.spec.whatwg.org/#response-class
@@ -40,9 +38,10 @@ class Response {
40
38
  // The static error() method steps are to return the result of creating a
41
39
  // Response object, given a new network error, "immutable", and this’s
42
40
  // relevant Realm.
43
- const responseObject = new Response()
41
+ const responseObject = new Response(kConstruct)
44
42
  responseObject[kState] = makeNetworkError()
45
43
  responseObject[kRealm] = relevantRealm
44
+ responseObject[kHeaders] = new Headers(kConstruct)
46
45
  responseObject[kHeaders][kHeadersList] = responseObject[kState].headersList
47
46
  responseObject[kHeaders][kGuard] = 'immutable'
48
47
  responseObject[kHeaders][kRealm] = relevantRealm
@@ -68,8 +67,11 @@ class Response {
68
67
  // 3. Let responseObject be the result of creating a Response object, given a new response,
69
68
  // "response", and this’s relevant Realm.
70
69
  const relevantRealm = { settingsObject: {} }
71
- const responseObject = new Response()
70
+ const responseObject = new Response(kConstruct)
71
+ responseObject[kState] = makeResponse({})
72
72
  responseObject[kRealm] = relevantRealm
73
+ responseObject[kHeaders] = new Headers(kConstruct)
74
+ responseObject[kHeaders][kHeadersList] = responseObject[kState].headersList
73
75
  responseObject[kHeaders][kGuard] = 'response'
74
76
  responseObject[kHeaders][kRealm] = relevantRealm
75
77
 
@@ -109,8 +111,11 @@ class Response {
109
111
 
110
112
  // 4. Let responseObject be the result of creating a Response object,
111
113
  // given a new response, "immutable", and this’s relevant Realm.
112
- const responseObject = new Response()
114
+ const responseObject = new Response(kConstruct)
115
+ responseObject[kState] = makeResponse({})
113
116
  responseObject[kRealm] = relevantRealm
117
+ responseObject[kHeaders] = new Headers(kConstruct)
118
+ responseObject[kHeaders][kHeadersList] = responseObject[kState].headersList
114
119
  responseObject[kHeaders][kGuard] = 'immutable'
115
120
  responseObject[kHeaders][kRealm] = relevantRealm
116
121
 
@@ -129,6 +134,10 @@ class Response {
129
134
 
130
135
  // https://fetch.spec.whatwg.org/#dom-response
131
136
  constructor (body = null, init = {}) {
137
+ if (body === kConstruct) {
138
+ return
139
+ }
140
+
132
141
  if (body !== null) {
133
142
  body = webidl.converters.BodyInit(body)
134
143
  }
@@ -260,9 +269,10 @@ class Response {
260
269
 
261
270
  // 3. Return the result of creating a Response object, given
262
271
  // clonedResponse, this’s headers’s guard, and this’s relevant Realm.
263
- const clonedResponseObject = new Response()
272
+ const clonedResponseObject = new Response(kConstruct)
264
273
  clonedResponseObject[kState] = clonedResponse
265
274
  clonedResponseObject[kRealm] = this[kRealm]
275
+ clonedResponseObject[kHeaders] = new Headers(kConstruct)
266
276
  clonedResponseObject[kHeaders][kHeadersList] = clonedResponse.headersList
267
277
  clonedResponseObject[kHeaders][kGuard] = this[kHeaders][kGuard]
268
278
  clonedResponseObject[kHeaders][kRealm] = this[kHeaders][kRealm]
@@ -335,10 +345,10 @@ function makeResponse (init) {
335
345
  cacheState: '',
336
346
  statusText: '',
337
347
  ...init,
338
- headersList: init.headersList
339
- ? new HeadersList(init.headersList)
348
+ headersList: init?.headersList
349
+ ? new HeadersList(init?.headersList)
340
350
  : new HeadersList(),
341
- urlList: init.urlList ? [...init.urlList] : []
351
+ urlList: init?.urlList ? [...init.urlList] : []
342
352
  }
343
353
  }
344
354