undici 7.0.0-alpha.1 → 7.0.0-alpha.2

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.
Files changed (71) hide show
  1. package/README.md +2 -2
  2. package/docs/docs/api/Client.md +1 -1
  3. package/docs/docs/api/Debug.md +1 -1
  4. package/docs/docs/api/Dispatcher.md +53 -2
  5. package/docs/docs/api/MockAgent.md +2 -0
  6. package/docs/docs/api/MockPool.md +2 -1
  7. package/docs/docs/api/RetryAgent.md +1 -1
  8. package/docs/docs/api/RetryHandler.md +1 -1
  9. package/docs/docs/api/WebSocket.md +45 -3
  10. package/index.js +6 -2
  11. package/lib/api/abort-signal.js +2 -0
  12. package/lib/api/api-pipeline.js +4 -2
  13. package/lib/api/api-request.js +4 -2
  14. package/lib/api/api-stream.js +3 -1
  15. package/lib/api/api-upgrade.js +2 -2
  16. package/lib/api/readable.js +194 -41
  17. package/lib/api/util.js +2 -0
  18. package/lib/core/connect.js +49 -22
  19. package/lib/core/constants.js +11 -9
  20. package/lib/core/diagnostics.js +122 -128
  21. package/lib/core/request.js +4 -4
  22. package/lib/core/symbols.js +2 -0
  23. package/lib/core/tree.js +4 -2
  24. package/lib/core/util.js +220 -39
  25. package/lib/dispatcher/client-h1.js +299 -60
  26. package/lib/dispatcher/client-h2.js +1 -1
  27. package/lib/dispatcher/client.js +24 -7
  28. package/lib/dispatcher/fixed-queue.js +91 -49
  29. package/lib/dispatcher/pool-stats.js +2 -0
  30. package/lib/dispatcher/proxy-agent.js +3 -1
  31. package/lib/handler/redirect-handler.js +2 -2
  32. package/lib/handler/retry-handler.js +2 -2
  33. package/lib/interceptor/dns.js +346 -0
  34. package/lib/mock/mock-agent.js +5 -8
  35. package/lib/mock/mock-client.js +7 -2
  36. package/lib/mock/mock-errors.js +3 -1
  37. package/lib/mock/mock-interceptor.js +8 -6
  38. package/lib/mock/mock-pool.js +7 -2
  39. package/lib/mock/mock-symbols.js +2 -1
  40. package/lib/mock/mock-utils.js +33 -5
  41. package/lib/util/timers.js +50 -6
  42. package/lib/web/cache/cache.js +24 -21
  43. package/lib/web/cache/cachestorage.js +1 -1
  44. package/lib/web/cookies/index.js +6 -4
  45. package/lib/web/fetch/body.js +42 -34
  46. package/lib/web/fetch/constants.js +35 -26
  47. package/lib/web/fetch/formdata-parser.js +14 -3
  48. package/lib/web/fetch/formdata.js +40 -20
  49. package/lib/web/fetch/headers.js +116 -84
  50. package/lib/web/fetch/index.js +65 -59
  51. package/lib/web/fetch/request.js +130 -55
  52. package/lib/web/fetch/response.js +79 -36
  53. package/lib/web/fetch/util.js +104 -57
  54. package/lib/web/fetch/webidl.js +38 -14
  55. package/lib/web/websocket/connection.js +92 -15
  56. package/lib/web/websocket/constants.js +2 -3
  57. package/lib/web/websocket/events.js +4 -2
  58. package/lib/web/websocket/receiver.js +20 -26
  59. package/lib/web/websocket/stream/websocketerror.js +83 -0
  60. package/lib/web/websocket/stream/websocketstream.js +485 -0
  61. package/lib/web/websocket/util.js +115 -10
  62. package/lib/web/websocket/websocket.js +45 -170
  63. package/package.json +6 -6
  64. package/types/interceptors.d.ts +14 -0
  65. package/types/mock-agent.d.ts +3 -0
  66. package/types/readable.d.ts +10 -7
  67. package/types/webidl.d.ts +24 -4
  68. package/types/websocket.d.ts +33 -0
  69. package/lib/mock/pluralizer.js +0 -29
  70. package/lib/web/cache/symbols.js +0 -5
  71. package/lib/web/fetch/symbols.js +0 -8
@@ -18,9 +18,7 @@ const {
18
18
  redirectStatusSet,
19
19
  nullBodyStatus
20
20
  } = require('./constants')
21
- const { kState, kHeaders } = require('./symbols')
22
21
  const { webidl } = require('./webidl')
23
- const { FormData } = require('./formdata')
24
22
  const { URLSerializer } = require('./data-url')
25
23
  const { kConstruct } = require('../../core/symbols')
26
24
  const assert = require('node:assert')
@@ -30,6 +28,11 @@ const textEncoder = new TextEncoder('utf-8')
30
28
 
31
29
  // https://fetch.spec.whatwg.org/#response-class
32
30
  class Response {
31
+ /** @type {Headers} */
32
+ #headers
33
+
34
+ #state
35
+
33
36
  // Creates network error Response.
34
37
  static error () {
35
38
  // The static error() method steps are to return the result of creating a
@@ -95,13 +98,13 @@ class Response {
95
98
  const responseObject = fromInnerResponse(makeResponse({}), 'immutable')
96
99
 
97
100
  // 5. Set responseObject’s response’s status to status.
98
- responseObject[kState].status = status
101
+ responseObject.#state.status = status
99
102
 
100
103
  // 6. Let value be parsedURL, serialized and isomorphic encoded.
101
104
  const value = isomorphicEncode(URLSerializer(parsedURL))
102
105
 
103
106
  // 7. Append `Location`/value to responseObject’s response’s header list.
104
- responseObject[kState].headersList.append('location', value, true)
107
+ responseObject.#state.headersList.append('location', value, true)
105
108
 
106
109
  // 8. Return responseObject.
107
110
  return responseObject
@@ -120,14 +123,14 @@ class Response {
120
123
  init = webidl.converters.ResponseInit(init)
121
124
 
122
125
  // 1. Set this’s response to a new response.
123
- this[kState] = makeResponse({})
126
+ this.#state = makeResponse({})
124
127
 
125
128
  // 2. Set this’s headers to a new Headers object with this’s relevant
126
129
  // Realm, whose header list is this’s response’s header list and guard
127
130
  // is "response".
128
- this[kHeaders] = new Headers(kConstruct)
129
- setHeadersGuard(this[kHeaders], 'response')
130
- setHeadersList(this[kHeaders], this[kState].headersList)
131
+ this.#headers = new Headers(kConstruct)
132
+ setHeadersGuard(this.#headers, 'response')
133
+ setHeadersList(this.#headers, this.#state.headersList)
131
134
 
132
135
  // 3. Let bodyWithType be null.
133
136
  let bodyWithType = null
@@ -147,14 +150,14 @@ class Response {
147
150
  webidl.brandCheck(this, Response)
148
151
 
149
152
  // The type getter steps are to return this’s response’s type.
150
- return this[kState].type
153
+ return this.#state.type
151
154
  }
152
155
 
153
156
  // Returns response’s URL, if it has one; otherwise the empty string.
154
157
  get url () {
155
158
  webidl.brandCheck(this, Response)
156
159
 
157
- const urlList = this[kState].urlList
160
+ const urlList = this.#state.urlList
158
161
 
159
162
  // The url getter steps are to return the empty string if this’s
160
163
  // response’s URL is null; otherwise this’s response’s URL,
@@ -174,7 +177,7 @@ class Response {
174
177
 
175
178
  // The redirected getter steps are to return true if this’s response’s URL
176
179
  // list has more than one item; otherwise false.
177
- return this[kState].urlList.length > 1
180
+ return this.#state.urlList.length > 1
178
181
  }
179
182
 
180
183
  // Returns response’s status.
@@ -182,7 +185,7 @@ class Response {
182
185
  webidl.brandCheck(this, Response)
183
186
 
184
187
  // The status getter steps are to return this’s response’s status.
185
- return this[kState].status
188
+ return this.#state.status
186
189
  }
187
190
 
188
191
  // Returns whether response’s status is an ok status.
@@ -191,7 +194,7 @@ class Response {
191
194
 
192
195
  // The ok getter steps are to return true if this’s response’s status is an
193
196
  // ok status; otherwise false.
194
- return this[kState].status >= 200 && this[kState].status <= 299
197
+ return this.#state.status >= 200 && this.#state.status <= 299
195
198
  }
196
199
 
197
200
  // Returns response’s status message.
@@ -200,7 +203,7 @@ class Response {
200
203
 
201
204
  // The statusText getter steps are to return this’s response’s status
202
205
  // message.
203
- return this[kState].statusText
206
+ return this.#state.statusText
204
207
  }
205
208
 
206
209
  // Returns response’s headers as Headers.
@@ -208,19 +211,19 @@ class Response {
208
211
  webidl.brandCheck(this, Response)
209
212
 
210
213
  // The headers getter steps are to return this’s headers.
211
- return this[kHeaders]
214
+ return this.#headers
212
215
  }
213
216
 
214
217
  get body () {
215
218
  webidl.brandCheck(this, Response)
216
219
 
217
- return this[kState].body ? this[kState].body.stream : null
220
+ return this.#state.body ? this.#state.body.stream : null
218
221
  }
219
222
 
220
223
  get bodyUsed () {
221
224
  webidl.brandCheck(this, Response)
222
225
 
223
- return !!this[kState].body && util.isDisturbed(this[kState].body.stream)
226
+ return !!this.#state.body && util.isDisturbed(this.#state.body.stream)
224
227
  }
225
228
 
226
229
  // Returns a clone of response.
@@ -228,7 +231,7 @@ class Response {
228
231
  webidl.brandCheck(this, Response)
229
232
 
230
233
  // 1. If this is unusable, then throw a TypeError.
231
- if (bodyUnusable(this)) {
234
+ if (bodyUnusable(this.#state)) {
232
235
  throw webidl.errors.exception({
233
236
  header: 'Response.clone',
234
237
  message: 'Body has already been consumed.'
@@ -236,11 +239,11 @@ class Response {
236
239
  }
237
240
 
238
241
  // 2. Let clonedResponse be the result of cloning this’s response.
239
- const clonedResponse = cloneResponse(this[kState])
242
+ const clonedResponse = cloneResponse(this.#state)
240
243
 
241
244
  // 3. Return the result of creating a Response object, given
242
245
  // clonedResponse, this’s headers’s guard, and this’s relevant Realm.
243
- return fromInnerResponse(clonedResponse, getHeadersGuard(this[kHeaders]))
246
+ return fromInnerResponse(clonedResponse, getHeadersGuard(this.#headers))
244
247
  }
245
248
 
246
249
  [nodeUtil.inspect.custom] (depth, options) {
@@ -264,9 +267,45 @@ class Response {
264
267
 
265
268
  return `Response ${nodeUtil.formatWithOptions(options, properties)}`
266
269
  }
270
+
271
+ /**
272
+ * @param {Response} response
273
+ */
274
+ static getResponseHeaders (response) {
275
+ return response.#headers
276
+ }
277
+
278
+ /**
279
+ * @param {Response} response
280
+ * @param {Headers} newHeaders
281
+ */
282
+ static setResponseHeaders (response, newHeaders) {
283
+ response.#headers = newHeaders
284
+ }
285
+
286
+ /**
287
+ * @param {Response} response
288
+ */
289
+ static getResponseState (response) {
290
+ return response.#state
291
+ }
292
+
293
+ /**
294
+ * @param {Response} response
295
+ * @param {any} newState
296
+ */
297
+ static setResponseState (response, newState) {
298
+ response.#state = newState
299
+ }
267
300
  }
268
301
 
269
- mixinBody(Response)
302
+ const { getResponseHeaders, setResponseHeaders, getResponseState, setResponseState } = Response
303
+ Reflect.deleteProperty(Response, 'getResponseHeaders')
304
+ Reflect.deleteProperty(Response, 'setResponseHeaders')
305
+ Reflect.deleteProperty(Response, 'getResponseState')
306
+ Reflect.deleteProperty(Response, 'setResponseState')
307
+
308
+ mixinBody(Response, getResponseState)
270
309
 
271
310
  Object.defineProperties(Response.prototype, {
272
311
  type: kEnumerableProperty,
@@ -463,17 +502,17 @@ function initializeResponse (response, init, body) {
463
502
 
464
503
  // 3. Set response’s response’s status to init["status"].
465
504
  if ('status' in init && init.status != null) {
466
- response[kState].status = init.status
505
+ getResponseState(response).status = init.status
467
506
  }
468
507
 
469
508
  // 4. Set response’s response’s status message to init["statusText"].
470
509
  if ('statusText' in init && init.statusText != null) {
471
- response[kState].statusText = init.statusText
510
+ getResponseState(response).statusText = init.statusText
472
511
  }
473
512
 
474
513
  // 5. If init["headers"] exists, then fill response’s headers with init["headers"].
475
514
  if ('headers' in init && init.headers != null) {
476
- fill(response[kHeaders], init.headers)
515
+ fill(getResponseHeaders(response), init.headers)
477
516
  }
478
517
 
479
518
  // 6. If body was given, then:
@@ -487,12 +526,12 @@ function initializeResponse (response, init, body) {
487
526
  }
488
527
 
489
528
  // 2. Set response's body to body's body.
490
- response[kState].body = body.body
529
+ getResponseState(response).body = body.body
491
530
 
492
531
  // 3. If body's type is non-null and response's header list does not contain
493
532
  // `Content-Type`, then append (`Content-Type`, body's type) to response's header list.
494
- if (body.type != null && !response[kState].headersList.contains('content-type', true)) {
495
- response[kState].headersList.append('content-type', body.type, true)
533
+ if (body.type != null && !getResponseState(response).headersList.contains('content-type', true)) {
534
+ getResponseState(response).headersList.append('content-type', body.type, true)
496
535
  }
497
536
  }
498
537
  }
@@ -505,10 +544,11 @@ function initializeResponse (response, init, body) {
505
544
  */
506
545
  function fromInnerResponse (innerResponse, guard) {
507
546
  const response = new Response(kConstruct)
508
- response[kState] = innerResponse
509
- response[kHeaders] = new Headers(kConstruct)
510
- setHeadersList(response[kHeaders], innerResponse.headersList)
511
- setHeadersGuard(response[kHeaders], guard)
547
+ setResponseState(response, innerResponse)
548
+ const headers = new Headers(kConstruct)
549
+ setResponseHeaders(response, headers)
550
+ setHeadersList(headers, innerResponse.headersList)
551
+ setHeadersGuard(headers, guard)
512
552
 
513
553
  if (hasFinalizationRegistry && innerResponse.body?.stream) {
514
554
  // If the target (response) is reclaimed, the cleanup callback may be called at some point with
@@ -528,7 +568,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
528
568
  return webidl.converters.USVString(V, prefix, name)
529
569
  }
530
570
 
531
- if (V instanceof Blob) {
571
+ if (webidl.is.Blob(V)) {
532
572
  return V
533
573
  }
534
574
 
@@ -536,11 +576,11 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
536
576
  return V
537
577
  }
538
578
 
539
- if (V instanceof FormData) {
579
+ if (webidl.is.FormData(V)) {
540
580
  return V
541
581
  }
542
582
 
543
- if (V instanceof URLSearchParams) {
583
+ if (webidl.is.URLSearchParams(V)) {
544
584
  return V
545
585
  }
546
586
 
@@ -549,7 +589,7 @@ webidl.converters.XMLHttpRequestBodyInit = function (V, prefix, name) {
549
589
 
550
590
  // https://fetch.spec.whatwg.org/#bodyinit
551
591
  webidl.converters.BodyInit = function (V, prefix, argument) {
552
- if (V instanceof ReadableStream) {
592
+ if (webidl.is.ReadableStream(V)) {
553
593
  return V
554
594
  }
555
595
 
@@ -579,6 +619,8 @@ webidl.converters.ResponseInit = webidl.dictionaryConverter([
579
619
  }
580
620
  ])
581
621
 
622
+ webidl.is.Response = webidl.util.MakeTypeAssertion(Response.prototype)
623
+
582
624
  module.exports = {
583
625
  isNetworkError,
584
626
  makeNetworkError,
@@ -587,5 +629,6 @@ module.exports = {
587
629
  filterResponse,
588
630
  Response,
589
631
  cloneResponse,
590
- fromInnerResponse
632
+ fromInnerResponse,
633
+ getResponseState
591
634
  }
@@ -399,7 +399,7 @@ function determineRequestsReferrer (request) {
399
399
 
400
400
  // note: we need to clone it as it's mutated
401
401
  referrerSource = new URL(globalOrigin)
402
- } else if (request.referrer instanceof URL) {
402
+ } else if (webidl.is.URL(request.referrer)) {
403
403
  // Let referrerSource be request’s referrer.
404
404
  referrerSource = request.referrer
405
405
  }
@@ -418,18 +418,37 @@ function determineRequestsReferrer (request) {
418
418
  referrerURL = referrerOrigin
419
419
  }
420
420
 
421
- const areSameOrigin = sameOrigin(request, referrerURL)
422
- const isNonPotentiallyTrustWorthy = isURLPotentiallyTrustworthy(referrerURL) &&
423
- !isURLPotentiallyTrustworthy(request.url)
421
+ // 7. The user agent MAY alter referrerURL or referrerOrigin at this point
422
+ // to enforce arbitrary policy considerations in the interests of minimizing
423
+ // data leakage. For example, the user agent could strip the URL down to an
424
+ // origin, modify its host, replace it with an empty string, etc.
424
425
 
425
426
  // 8. Execute the switch statements corresponding to the value of policy:
426
427
  switch (policy) {
427
- case 'origin': return referrerOrigin != null ? referrerOrigin : stripURLForReferrer(referrerSource, true)
428
- case 'unsafe-url': return referrerURL
429
- case 'same-origin':
430
- return areSameOrigin ? referrerOrigin : 'no-referrer'
431
- case 'origin-when-cross-origin':
432
- return areSameOrigin ? referrerURL : referrerOrigin
428
+ case 'no-referrer':
429
+ // Return no referrer
430
+ return 'no-referrer'
431
+ case 'origin':
432
+ // Return referrerOrigin
433
+ if (referrerOrigin != null) {
434
+ return referrerOrigin
435
+ }
436
+ return stripURLForReferrer(referrerSource, true)
437
+ case 'unsafe-url':
438
+ // Return referrerURL.
439
+ return referrerURL
440
+ case 'strict-origin': {
441
+ const currentURL = requestCurrentURL(request)
442
+
443
+ // 1. If referrerURL is a potentially trustworthy URL and request’s
444
+ // current URL is not a potentially trustworthy URL, then return no
445
+ // referrer.
446
+ if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {
447
+ return 'no-referrer'
448
+ }
449
+ // 2. Return referrerOrigin
450
+ return referrerOrigin
451
+ }
433
452
  case 'strict-origin-when-cross-origin': {
434
453
  const currentURL = requestCurrentURL(request)
435
454
 
@@ -449,23 +468,34 @@ function determineRequestsReferrer (request) {
449
468
  // 3. Return referrerOrigin.
450
469
  return referrerOrigin
451
470
  }
452
- case 'strict-origin':
453
- /**
454
- * 1. If referrerURL is a potentially trustworthy URL and
455
- * request’s current URL is not a potentially trustworthy URL,
456
- * then return no referrer.
457
- * 2. Return referrerOrigin
458
- */
459
- case 'no-referrer-when-downgrade': // eslint-disable-line
460
- /**
461
- * 1. If referrerURL is a potentially trustworthy URL and
462
- * request’s current URL is not a potentially trustworthy URL,
463
- * then return no referrer.
464
- * 2. Return referrerOrigin
465
- */
466
-
467
- default: // eslint-disable-line
468
- return isNonPotentiallyTrustWorthy ? 'no-referrer' : referrerOrigin
471
+ case 'same-origin':
472
+ // 1. If the origin of referrerURL and the origin of request’s current
473
+ // URL are the same, then return referrerURL.
474
+ if (sameOrigin(request, referrerURL)) {
475
+ return referrerURL
476
+ }
477
+ // 2. Return no referrer.
478
+ return 'no-referrer'
479
+ case 'origin-when-cross-origin':
480
+ // 1. If the origin of referrerURL and the origin of request’s current
481
+ // URL are the same, then return referrerURL.
482
+ if (sameOrigin(request, referrerURL)) {
483
+ return referrerURL
484
+ }
485
+ // 2. Return referrerOrigin.
486
+ return referrerOrigin
487
+ case 'no-referrer-when-downgrade': {
488
+ const currentURL = requestCurrentURL(request)
489
+
490
+ // 1. If referrerURL is a potentially trustworthy URL and request’s
491
+ // current URL is not a potentially trustworthy URL, then return no
492
+ // referrer.
493
+ if (isURLPotentiallyTrustworthy(referrerURL) && !isURLPotentiallyTrustworthy(currentURL)) {
494
+ return 'no-referrer'
495
+ }
496
+ // 2. Return referrerOrigin
497
+ return referrerOrigin
498
+ }
469
499
  }
470
500
  }
471
501
 
@@ -476,7 +506,7 @@ function determineRequestsReferrer (request) {
476
506
  */
477
507
  function stripURLForReferrer (url, originOnly) {
478
508
  // 1. Assert: url is a URL.
479
- assert(url instanceof URL)
509
+ assert(webidl.is.URL(url))
480
510
 
481
511
  url = new URL(url)
482
512
 
@@ -508,7 +538,7 @@ function stripURLForReferrer (url, originOnly) {
508
538
  }
509
539
 
510
540
  function isURLPotentiallyTrustworthy (url) {
511
- if (!(url instanceof URL)) {
541
+ if (!webidl.is.URL(url)) {
512
542
  return false
513
543
  }
514
544
 
@@ -825,7 +855,7 @@ const esIteratorPrototype = Object.getPrototypeOf(Object.getPrototypeOf([][Symbo
825
855
  /**
826
856
  * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
827
857
  * @param {string} name name of the instance
828
- * @param {symbol} kInternalIterator
858
+ * @param {((target: any) => any)} kInternalIterator
829
859
  * @param {string | number} [keyIndex]
830
860
  * @param {string | number} [valueIndex]
831
861
  */
@@ -867,7 +897,7 @@ function createIterator (name, kInternalIterator, keyIndex = 0, valueIndex = 1)
867
897
  // 7. Let kind be object’s kind.
868
898
  // 8. Let values be object’s target's value pairs to iterate over.
869
899
  const index = this.#index
870
- const values = this.#target[kInternalIterator]
900
+ const values = kInternalIterator(this.#target)
871
901
 
872
902
  // 9. Let len be the length of values.
873
903
  const len = values.length
@@ -961,7 +991,7 @@ function createIterator (name, kInternalIterator, keyIndex = 0, valueIndex = 1)
961
991
  * @see https://webidl.spec.whatwg.org/#dfn-iterator-prototype-object
962
992
  * @param {string} name name of the instance
963
993
  * @param {any} object class
964
- * @param {symbol} kInternalIterator
994
+ * @param {(target: any) => any} kInternalIterator
965
995
  * @param {string | number} [keyIndex]
966
996
  * @param {string | number} [valueIndex]
967
997
  */
@@ -1029,7 +1059,7 @@ function iteratorMixin (name, object, kInternalIterator, keyIndex = 0, valueInde
1029
1059
  /**
1030
1060
  * @see https://fetch.spec.whatwg.org/#body-fully-read
1031
1061
  */
1032
- async function fullyReadBody (body, processBody, processBodyError) {
1062
+ function fullyReadBody (body, processBody, processBodyError) {
1033
1063
  // 1. If taskDestination is null, then set taskDestination to
1034
1064
  // the result of starting a new parallel queue.
1035
1065
 
@@ -1054,11 +1084,7 @@ async function fullyReadBody (body, processBody, processBodyError) {
1054
1084
  }
1055
1085
 
1056
1086
  // 5. Read all bytes from reader, given successSteps and errorSteps.
1057
- try {
1058
- successSteps(await readAllBytes(reader))
1059
- } catch (e) {
1060
- errorSteps(e)
1061
- }
1087
+ readAllBytes(reader, successSteps, errorSteps)
1062
1088
  }
1063
1089
 
1064
1090
  /**
@@ -1096,30 +1122,39 @@ function isomorphicEncode (input) {
1096
1122
  * @see https://streams.spec.whatwg.org/#readablestreamdefaultreader-read-all-bytes
1097
1123
  * @see https://streams.spec.whatwg.org/#read-loop
1098
1124
  * @param {ReadableStreamDefaultReader} reader
1125
+ * @param {(bytes: Uint8Array) => void} successSteps
1126
+ * @param {(error: Error) => void} failureSteps
1099
1127
  */
1100
- async function readAllBytes (reader) {
1128
+ async function readAllBytes (reader, successSteps, failureSteps) {
1101
1129
  const bytes = []
1102
1130
  let byteLength = 0
1103
1131
 
1104
- while (true) {
1105
- const { done, value: chunk } = await reader.read()
1132
+ try {
1133
+ do {
1134
+ const { done, value: chunk } = await reader.read()
1106
1135
 
1107
- if (done) {
1108
- // 1. Call successSteps with bytes.
1109
- return Buffer.concat(bytes, byteLength)
1110
- }
1136
+ if (done) {
1137
+ // 1. Call successSteps with bytes.
1138
+ successSteps(Buffer.concat(bytes, byteLength))
1139
+ return
1140
+ }
1111
1141
 
1112
- // 1. If chunk is not a Uint8Array object, call failureSteps
1113
- // with a TypeError and abort these steps.
1114
- if (!isUint8Array(chunk)) {
1115
- throw new TypeError('Received non-Uint8Array chunk')
1116
- }
1142
+ // 1. If chunk is not a Uint8Array object, call failureSteps
1143
+ // with a TypeError and abort these steps.
1144
+ if (!isUint8Array(chunk)) {
1145
+ failureSteps(TypeError('Received non-Uint8Array chunk'))
1146
+ return
1147
+ }
1117
1148
 
1118
- // 2. Append the bytes represented by chunk to bytes.
1119
- bytes.push(chunk)
1120
- byteLength += chunk.length
1149
+ // 2. Append the bytes represented by chunk to bytes.
1150
+ bytes.push(chunk)
1151
+ byteLength += chunk.length
1121
1152
 
1122
1153
  // 3. Read-loop given reader, bytes, successSteps, and failureSteps.
1154
+ } while (true)
1155
+ } catch (e) {
1156
+ // 1. Call failureSteps with e.
1157
+ failureSteps(e)
1123
1158
  }
1124
1159
  }
1125
1160
 
@@ -1333,6 +1368,14 @@ function buildContentRange (rangeStart, rangeEnd, fullLength) {
1333
1368
  // interpreted as a zlib stream, otherwise it's interpreted as a
1334
1369
  // raw deflate stream.
1335
1370
  class InflateStream extends Transform {
1371
+ #zlibOptions
1372
+
1373
+ /** @param {zlib.ZlibOptions} [zlibOptions] */
1374
+ constructor (zlibOptions) {
1375
+ super()
1376
+ this.#zlibOptions = zlibOptions
1377
+ }
1378
+
1336
1379
  _transform (chunk, encoding, callback) {
1337
1380
  if (!this._inflateStream) {
1338
1381
  if (chunk.length === 0) {
@@ -1340,8 +1383,8 @@ class InflateStream extends Transform {
1340
1383
  return
1341
1384
  }
1342
1385
  this._inflateStream = (chunk[0] & 0x0F) === 0x08
1343
- ? zlib.createInflate()
1344
- : zlib.createInflateRaw()
1386
+ ? zlib.createInflate(this.#zlibOptions)
1387
+ : zlib.createInflateRaw(this.#zlibOptions)
1345
1388
 
1346
1389
  this._inflateStream.on('data', this.push.bind(this))
1347
1390
  this._inflateStream.on('end', () => this.push(null))
@@ -1360,8 +1403,12 @@ class InflateStream extends Transform {
1360
1403
  }
1361
1404
  }
1362
1405
 
1363
- function createInflate () {
1364
- return new InflateStream()
1406
+ /**
1407
+ * @param {zlib.ZlibOptions} [zlibOptions]
1408
+ * @returns {InflateStream}
1409
+ */
1410
+ function createInflate (zlibOptions) {
1411
+ return new InflateStream(zlibOptions)
1365
1412
  }
1366
1413
 
1367
1414
  /**
@@ -13,10 +13,12 @@ const NULL = 7
13
13
  const OBJECT = 8 // function and object
14
14
 
15
15
  /** @type {import('../../../types/webidl').Webidl} */
16
- const webidl = {}
17
- webidl.converters = {}
18
- webidl.util = {}
19
- webidl.errors = {}
16
+ const webidl = {
17
+ converters: {},
18
+ util: {},
19
+ errors: {},
20
+ is: {}
21
+ }
20
22
 
21
23
  webidl.errors.exception = function (message) {
22
24
  return new TypeError(`${message.header}: ${message.message}`)
@@ -43,18 +45,22 @@ webidl.errors.invalidArgument = function (context) {
43
45
 
44
46
  // https://webidl.spec.whatwg.org/#implements
45
47
  webidl.brandCheck = function (V, I) {
46
- if (!(V instanceof I)) {
48
+ if (!I.prototype.isPrototypeOf(V)) { // eslint-disable-line no-prototype-builtins
47
49
  const err = new TypeError('Illegal invocation')
48
50
  err.code = 'ERR_INVALID_THIS' // node compat.
49
51
  throw err
50
52
  }
51
53
  }
52
54
 
53
- webidl.brandCheckMultiple = function (V, List) {
54
- if (List.every((clazz) => !(V instanceof clazz))) {
55
- const err = new TypeError('Illegal invocation')
56
- err.code = 'ERR_INVALID_THIS' // node compat.
57
- throw err
55
+ webidl.brandCheckMultiple = function (List) {
56
+ const prototypes = List.map((c) => webidl.util.MakeTypeAssertion(c.prototype))
57
+
58
+ return (V) => {
59
+ if (prototypes.every(typeCheck => !typeCheck(V))) {
60
+ const err = new TypeError('Illegal invocation')
61
+ err.code = 'ERR_INVALID_THIS' // node compat.
62
+ throw err
63
+ }
58
64
  }
59
65
  }
60
66
 
@@ -75,6 +81,11 @@ webidl.illegalConstructor = function () {
75
81
  })
76
82
  }
77
83
 
84
+ const isPrototypeOf = Object.prototype.isPrototypeOf
85
+ webidl.util.MakeTypeAssertion = function (Prototype) {
86
+ return (O) => isPrototypeOf.call(Prototype, O)
87
+ }
88
+
78
89
  // https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
79
90
  webidl.util.Type = function (V) {
80
91
  switch (typeof V) {
@@ -372,12 +383,12 @@ webidl.recordConverter = function (keyConverter, valueConverter) {
372
383
  }
373
384
  }
374
385
 
375
- webidl.interfaceConverter = function (i) {
386
+ webidl.interfaceConverter = function (TypeCheck, name) {
376
387
  return (V, prefix, argument) => {
377
- if (!(V instanceof i)) {
388
+ if (!TypeCheck(V)) {
378
389
  throw webidl.errors.exception({
379
390
  header: prefix,
380
- message: `Expected ${argument} ("${webidl.util.Stringify(V)}") to be an instance of ${i.name}.`
391
+ message: `Expected ${argument} ("${webidl.util.Stringify(V)}") to be an instance of ${name}.`
381
392
  })
382
393
  }
383
394
 
@@ -451,6 +462,14 @@ webidl.nullableConverter = function (converter) {
451
462
  }
452
463
  }
453
464
 
465
+ webidl.is.ReadableStream = webidl.util.MakeTypeAssertion(ReadableStream.prototype)
466
+ webidl.is.Blob = webidl.util.MakeTypeAssertion(Blob.prototype)
467
+ webidl.is.URLSearchParams = webidl.util.MakeTypeAssertion(URLSearchParams.prototype)
468
+ webidl.is.File = webidl.util.MakeTypeAssertion((globalThis.File ?? require('node:buffer').File).prototype)
469
+ webidl.is.URL = webidl.util.MakeTypeAssertion(URL.prototype)
470
+ webidl.is.AbortSignal = webidl.util.MakeTypeAssertion(AbortSignal.prototype)
471
+ webidl.is.MessagePort = webidl.util.MakeTypeAssertion(MessagePort.prototype)
472
+
454
473
  // https://webidl.spec.whatwg.org/#es-DOMString
455
474
  webidl.converters.DOMString = function (V, prefix, argument, opts) {
456
475
  // 1. If V is null and the conversion is to an IDL type
@@ -697,7 +716,12 @@ webidl.converters['record<ByteString, ByteString>'] = webidl.recordConverter(
697
716
  webidl.converters.ByteString
698
717
  )
699
718
 
700
- webidl.converters.Blob = webidl.interfaceConverter(Blob)
719
+ webidl.converters.Blob = webidl.interfaceConverter(webidl.is.Blob, 'Blob')
720
+
721
+ webidl.converters.AbortSignal = webidl.interfaceConverter(
722
+ webidl.is.AbortSignal,
723
+ 'AbortSignal'
724
+ )
701
725
 
702
726
  module.exports = {
703
727
  webidl