happy-dom 13.4.0 → 13.5.0

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.

Potentially problematic release.


This version of happy-dom might be problematic. Click here for more details.

Files changed (86) hide show
  1. package/README.md +3 -0
  2. package/cjs/async-task-manager/AsyncTaskManager.cjs +14 -1
  3. package/cjs/async-task-manager/AsyncTaskManager.cjs.map +1 -1
  4. package/cjs/async-task-manager/AsyncTaskManager.d.ts.map +1 -1
  5. package/cjs/fetch/Fetch.cjs +60 -23
  6. package/cjs/fetch/Fetch.cjs.map +1 -1
  7. package/cjs/fetch/Fetch.d.ts +11 -0
  8. package/cjs/fetch/Fetch.d.ts.map +1 -1
  9. package/cjs/fetch/Request.d.ts +2 -2
  10. package/cjs/fetch/Request.d.ts.map +1 -1
  11. package/cjs/fetch/Response.cjs +2 -0
  12. package/cjs/fetch/Response.cjs.map +1 -1
  13. package/cjs/fetch/Response.d.ts +3 -2
  14. package/cjs/fetch/Response.d.ts.map +1 -1
  15. package/cjs/fetch/multipart/MultipartFormDataParser.cjs +19 -24
  16. package/cjs/fetch/multipart/MultipartFormDataParser.cjs.map +1 -1
  17. package/cjs/fetch/multipart/MultipartFormDataParser.d.ts +4 -3
  18. package/cjs/fetch/multipart/MultipartFormDataParser.d.ts.map +1 -1
  19. package/cjs/fetch/types/IRequest.d.ts +2 -2
  20. package/cjs/fetch/types/IRequest.d.ts.map +1 -1
  21. package/cjs/fetch/types/IRequestBody.d.ts +2 -2
  22. package/cjs/fetch/types/IRequestBody.d.ts.map +1 -1
  23. package/cjs/fetch/types/IResponse.d.ts +2 -2
  24. package/cjs/fetch/types/IResponse.d.ts.map +1 -1
  25. package/cjs/fetch/types/IResponseBody.d.ts +2 -2
  26. package/cjs/fetch/types/IResponseBody.d.ts.map +1 -1
  27. package/cjs/fetch/utilities/FetchBodyUtility.cjs +44 -22
  28. package/cjs/fetch/utilities/FetchBodyUtility.cjs.map +1 -1
  29. package/cjs/fetch/utilities/FetchBodyUtility.d.ts +15 -5
  30. package/cjs/fetch/utilities/FetchBodyUtility.d.ts.map +1 -1
  31. package/cjs/version.cjs +1 -1
  32. package/cjs/window/BrowserWindow.cjs +2 -1
  33. package/cjs/window/BrowserWindow.cjs.map +1 -1
  34. package/cjs/window/BrowserWindow.d.ts +7 -1
  35. package/cjs/window/BrowserWindow.d.ts.map +1 -1
  36. package/cjs/window/IBrowserWindow.d.ts +3 -1
  37. package/cjs/window/IBrowserWindow.d.ts.map +1 -1
  38. package/lib/async-task-manager/AsyncTaskManager.d.ts.map +1 -1
  39. package/lib/async-task-manager/AsyncTaskManager.js +14 -1
  40. package/lib/async-task-manager/AsyncTaskManager.js.map +1 -1
  41. package/lib/fetch/Fetch.d.ts +11 -0
  42. package/lib/fetch/Fetch.d.ts.map +1 -1
  43. package/lib/fetch/Fetch.js +56 -19
  44. package/lib/fetch/Fetch.js.map +1 -1
  45. package/lib/fetch/Request.d.ts +2 -2
  46. package/lib/fetch/Request.d.ts.map +1 -1
  47. package/lib/fetch/Response.d.ts +3 -2
  48. package/lib/fetch/Response.d.ts.map +1 -1
  49. package/lib/fetch/Response.js +2 -0
  50. package/lib/fetch/Response.js.map +1 -1
  51. package/lib/fetch/multipart/MultipartFormDataParser.d.ts +4 -3
  52. package/lib/fetch/multipart/MultipartFormDataParser.d.ts.map +1 -1
  53. package/lib/fetch/multipart/MultipartFormDataParser.js +14 -19
  54. package/lib/fetch/multipart/MultipartFormDataParser.js.map +1 -1
  55. package/lib/fetch/types/IRequest.d.ts +2 -2
  56. package/lib/fetch/types/IRequest.d.ts.map +1 -1
  57. package/lib/fetch/types/IRequestBody.d.ts +2 -2
  58. package/lib/fetch/types/IRequestBody.d.ts.map +1 -1
  59. package/lib/fetch/types/IResponse.d.ts +2 -2
  60. package/lib/fetch/types/IResponse.d.ts.map +1 -1
  61. package/lib/fetch/types/IResponseBody.d.ts +2 -2
  62. package/lib/fetch/types/IResponseBody.d.ts.map +1 -1
  63. package/lib/fetch/utilities/FetchBodyUtility.d.ts +15 -5
  64. package/lib/fetch/utilities/FetchBodyUtility.d.ts.map +1 -1
  65. package/lib/fetch/utilities/FetchBodyUtility.js +44 -22
  66. package/lib/fetch/utilities/FetchBodyUtility.js.map +1 -1
  67. package/lib/version.js +1 -1
  68. package/lib/window/BrowserWindow.d.ts +7 -1
  69. package/lib/window/BrowserWindow.d.ts.map +1 -1
  70. package/lib/window/BrowserWindow.js +2 -1
  71. package/lib/window/BrowserWindow.js.map +1 -1
  72. package/lib/window/IBrowserWindow.d.ts +3 -1
  73. package/lib/window/IBrowserWindow.d.ts.map +1 -1
  74. package/package.json +1 -1
  75. package/src/async-task-manager/AsyncTaskManager.ts +17 -2
  76. package/src/fetch/Fetch.ts +60 -20
  77. package/src/fetch/Request.ts +2 -2
  78. package/src/fetch/Response.ts +5 -2
  79. package/src/fetch/multipart/MultipartFormDataParser.ts +16 -28
  80. package/src/fetch/types/IRequest.ts +2 -2
  81. package/src/fetch/types/IRequestBody.ts +2 -2
  82. package/src/fetch/types/IResponse.ts +2 -2
  83. package/src/fetch/types/IResponseBody.ts +2 -2
  84. package/src/fetch/utilities/FetchBodyUtility.ts +52 -35
  85. package/src/window/BrowserWindow.ts +2 -1
  86. package/src/window/IBrowserWindow.ts +2 -1
@@ -1,9 +1,10 @@
1
1
  import FormData from '../../form-data/FormData.js';
2
+ import { ReadableStream } from 'stream/web';
2
3
  import * as PropertySymbol from '../../PropertySymbol.js';
3
- import Stream from 'stream';
4
4
  import MultipartReader from './MultipartReader.js';
5
5
  import DOMException from '../../exception/DOMException.js';
6
6
  import DOMExceptionNameEnum from '../../exception/DOMExceptionNameEnum.js';
7
+ import { Buffer } from 'buffer';
7
8
 
8
9
  /**
9
10
  * Multipart form data factory.
@@ -20,7 +21,7 @@ export default class MultipartFormDataParser {
20
21
  * @returns Form data.
21
22
  */
22
23
  public static async streamToFormData(
23
- body: Stream.Readable,
24
+ body: ReadableStream,
24
25
  contentType: string
25
26
  ): Promise<{ formData: FormData; buffer: Buffer }> {
26
27
  if (!/multipart/i.test(contentType)) {
@@ -39,35 +40,17 @@ export default class MultipartFormDataParser {
39
40
  );
40
41
  }
41
42
 
43
+ const bodyReader = body.getReader();
42
44
  const reader = new MultipartReader(match[1] || match[2]);
43
45
  const chunks = [];
44
46
  let buffer: Buffer;
45
- let bytes = 0;
47
+ const bytes = 0;
46
48
 
47
- try {
48
- for await (const chunk of body) {
49
- reader.write(chunk);
50
- bytes += chunk.length;
51
- chunks.push(chunk);
52
- }
53
- } catch (error) {
54
- if (error instanceof DOMException) {
55
- throw error;
56
- }
57
- throw new DOMException(
58
- `Failed to read response body. Error: ${error.message}.`,
59
- DOMExceptionNameEnum.encodingError
60
- );
61
- }
49
+ let readResult = await bodyReader.read();
62
50
 
63
- if (
64
- (<Stream.Readable>body).readableEnded === false ||
65
- (<Stream.Readable>body)['_readableState']?.ended === false
66
- ) {
67
- throw new DOMException(
68
- `Premature close of server response.`,
69
- DOMExceptionNameEnum.invalidStateError
70
- );
51
+ while (!readResult.done) {
52
+ reader.write(readResult.value);
53
+ readResult = await bodyReader.read();
71
54
  }
72
55
 
73
56
  try {
@@ -96,7 +79,7 @@ export default class MultipartFormDataParser {
96
79
  contentType: string;
97
80
  contentLength: number;
98
81
  buffer: Buffer;
99
- stream: Stream.Readable;
82
+ stream: ReadableStream;
100
83
  } {
101
84
  const boundary = '----HappyDOMFormDataBoundary' + Math.random().toString(36);
102
85
  const chunks: Buffer[] = [];
@@ -132,7 +115,12 @@ export default class MultipartFormDataParser {
132
115
  contentType: `multipart/form-data; boundary=${boundary}`,
133
116
  contentLength: buffer.length,
134
117
  buffer,
135
- stream: Stream.Readable.from(buffer)
118
+ stream: new ReadableStream({
119
+ start(controller) {
120
+ controller.enqueue(buffer);
121
+ controller.close();
122
+ }
123
+ })
136
124
  };
137
125
  }
138
126
 
@@ -1,7 +1,7 @@
1
1
  import IHeaders from './IHeaders.js';
2
2
  import IBlob from '../../file/IBlob.js';
3
3
  import AbortSignal from '../AbortSignal.js';
4
- import Stream from 'stream';
4
+ import { ReadableStream } from 'stream/web';
5
5
  import IRequestReferrerPolicy from './IRequestReferrerPolicy.js';
6
6
  import IRequestRedirect from './IRequestRedirect.js';
7
7
  import IRequestCredentials from './IRequestCredentials.js';
@@ -16,7 +16,7 @@ export default interface IRequest {
16
16
  readonly redirect: IRequestRedirect;
17
17
  readonly referrer: string;
18
18
  readonly url: string;
19
- readonly body: Stream.Readable | null;
19
+ readonly body: ReadableStream | null;
20
20
  readonly bodyUsed: boolean;
21
21
  readonly referrerPolicy: IRequestReferrerPolicy;
22
22
  readonly signal: AbortSignal | null;
@@ -1,12 +1,12 @@
1
1
  import { URLSearchParams } from 'url';
2
2
  import FormData from '../../form-data/FormData.js';
3
3
  import Blob from '../../file/Blob.js';
4
- import Stream from 'stream';
4
+ import { ReadableStream } from 'stream/web';
5
5
 
6
6
  type IRequestBody =
7
7
  | ArrayBuffer
8
8
  | ArrayBufferView
9
- | Stream.Readable
9
+ | ReadableStream
10
10
  | string
11
11
  | URLSearchParams
12
12
  | Blob
@@ -1,6 +1,6 @@
1
1
  import IHeaders from './IHeaders.js';
2
2
  import IBlob from '../../file/IBlob.js';
3
- import Stream from 'stream';
3
+ import { ReadableStream } from 'stream/web';
4
4
  import { Buffer } from 'buffer';
5
5
 
6
6
  /**
@@ -16,7 +16,7 @@ export default interface IResponse {
16
16
  readonly statusText: string;
17
17
  readonly type: 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect';
18
18
  readonly url: string;
19
- readonly body: Stream.Readable | null;
19
+ readonly body: ReadableStream | null;
20
20
  readonly bodyUsed: boolean;
21
21
 
22
22
  arrayBuffer(): Promise<ArrayBuffer>;
@@ -1,12 +1,12 @@
1
+ import { ReadableStream } from 'stream/web';
1
2
  import { URLSearchParams } from 'url';
2
3
  import FormData from '../../form-data/FormData.js';
3
4
  import Blob from '../../file/Blob.js';
4
- import Stream from 'stream';
5
5
 
6
6
  type IResponseBody =
7
7
  | ArrayBuffer
8
8
  | ArrayBufferView
9
- | Stream.Readable
9
+ | ReadableStream
10
10
  | string
11
11
  | URLSearchParams
12
12
  | Blob
@@ -1,6 +1,6 @@
1
1
  import MultipartFormDataParser from '../multipart/MultipartFormDataParser.js';
2
+ import { ReadableStream } from 'stream/web';
2
3
  import * as PropertySymbol from '../../PropertySymbol.js';
3
- import Stream from 'stream';
4
4
  import { URLSearchParams } from 'url';
5
5
  import FormData from '../../form-data/FormData.js';
6
6
  import Blob from '../../file/Blob.js';
@@ -14,6 +14,24 @@ import { Buffer } from 'buffer';
14
14
  * Fetch body utility.
15
15
  */
16
16
  export default class FetchBodyUtility {
17
+ /**
18
+ * Wraps a given value in a browser ReadableStream.
19
+ *
20
+ * This method creates a ReadableStream and immediately enqueues and closes it
21
+ * with the provided value, useful for stream API compatibility.
22
+ *
23
+ * @param value The value to be wrapped in a ReadableStream.
24
+ * @returns ReadableStream
25
+ */
26
+ public static toReadableStream(value): ReadableStream {
27
+ return new ReadableStream({
28
+ start(controller) {
29
+ controller.enqueue(value);
30
+ controller.close();
31
+ }
32
+ });
33
+ }
34
+
17
35
  /**
18
36
  * Parses body and returns stream and type.
19
37
  *
@@ -26,7 +44,7 @@ export default class FetchBodyUtility {
26
44
  public static getBodyStream(body: IRequestBody | IResponseBody): {
27
45
  contentType: string;
28
46
  contentLength: number | null;
29
- stream: Stream.Readable;
47
+ stream: ReadableStream | null;
30
48
  buffer: Buffer | null;
31
49
  } {
32
50
  if (body === null || body === undefined) {
@@ -35,7 +53,7 @@ export default class FetchBodyUtility {
35
53
  const buffer = Buffer.from(body.toString());
36
54
  return {
37
55
  buffer,
38
- stream: Stream.Readable.from(Buffer.from(buffer)),
56
+ stream: this.toReadableStream(buffer),
39
57
  contentType: 'application/x-www-form-urlencoded;charset=UTF-8',
40
58
  contentLength: buffer.length
41
59
  };
@@ -43,14 +61,14 @@ export default class FetchBodyUtility {
43
61
  const buffer = (<Blob>body)[PropertySymbol.buffer];
44
62
  return {
45
63
  buffer,
46
- stream: Stream.Readable.from(buffer),
47
- contentType: (<Blob>body).type,
64
+ stream: this.toReadableStream(buffer),
65
+ contentType: body.type,
48
66
  contentLength: body.size
49
67
  };
50
68
  } else if (Buffer.isBuffer(body)) {
51
69
  return {
52
70
  buffer: body,
53
- stream: Stream.Readable.from(body),
71
+ stream: this.toReadableStream(body),
54
72
  contentType: null,
55
73
  contentLength: body.length
56
74
  };
@@ -58,7 +76,7 @@ export default class FetchBodyUtility {
58
76
  const buffer = Buffer.from(body);
59
77
  return {
60
78
  buffer,
61
- stream: Stream.Readable.from(buffer),
79
+ stream: this.toReadableStream(buffer),
62
80
  contentType: null,
63
81
  contentLength: body.byteLength
64
82
  };
@@ -66,14 +84,14 @@ export default class FetchBodyUtility {
66
84
  const buffer = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
67
85
  return {
68
86
  buffer,
69
- stream: Stream.Readable.from(buffer),
87
+ stream: this.toReadableStream(buffer),
70
88
  contentType: null,
71
89
  contentLength: body.byteLength
72
90
  };
73
- } else if (body instanceof Stream.Stream) {
91
+ } else if (body instanceof ReadableStream) {
74
92
  return {
75
93
  buffer: null,
76
- stream: <Stream.Readable>body,
94
+ stream: body,
77
95
  contentType: null,
78
96
  contentLength: null
79
97
  };
@@ -84,7 +102,7 @@ export default class FetchBodyUtility {
84
102
  const buffer = Buffer.from(String(body));
85
103
  return {
86
104
  buffer,
87
- stream: Stream.Readable.from(buffer),
105
+ stream: this.toReadableStream(buffer),
88
106
  contentType: 'text/plain;charset=UTF-8',
89
107
  contentLength: buffer.length
90
108
  };
@@ -102,9 +120,9 @@ export default class FetchBodyUtility {
102
120
  * @returns New stream.
103
121
  */
104
122
  public static cloneBodyStream(requestOrResponse: {
105
- body: Stream.Readable;
123
+ body: ReadableStream;
106
124
  bodyUsed: boolean;
107
- }): Stream.Readable {
125
+ }): ReadableStream {
108
126
  if (requestOrResponse.bodyUsed) {
109
127
  throw new DOMException(
110
128
  `Failed to clone body stream of request: Request body is already used.`,
@@ -112,17 +130,15 @@ export default class FetchBodyUtility {
112
130
  );
113
131
  }
114
132
 
115
- const p1 = new Stream.PassThrough();
116
- const p2 = new Stream.PassThrough();
117
-
118
- requestOrResponse.body.pipe(p1);
119
- requestOrResponse.body.pipe(p2);
133
+ // Uses the tee() method to clone the ReadableStream
134
+ const [stream1, stream2] = requestOrResponse.body.tee();
120
135
 
121
- // Sets the body of the cloned request/response to the first pass through stream.
122
- (<Stream.Readable>requestOrResponse.body) = p1;
136
+ // Sets the body of the cloned request to the first pass through stream.
137
+ // TODO: check id this is required as request should be read only object
138
+ <ReadableStream>requestOrResponse.body == stream1;
123
139
 
124
- // Returns the clone.
125
- return p2;
140
+ // Returns the other stream as the clone
141
+ return stream2;
126
142
  }
127
143
 
128
144
  /**
@@ -135,18 +151,29 @@ export default class FetchBodyUtility {
135
151
  * @param body Body stream.
136
152
  * @returns Promise.
137
153
  */
138
- public static async consumeBodyStream(body: Stream.Readable | null): Promise<Buffer> {
139
- if (body === null || !(body instanceof Stream.Stream)) {
154
+ public static async consumeBodyStream(body: ReadableStream | null): Promise<Buffer> {
155
+ if (body === null || !(body instanceof ReadableStream)) {
140
156
  return Buffer.alloc(0);
141
157
  }
142
158
 
159
+ if (body[PropertySymbol.error]) {
160
+ throw body[PropertySymbol.error];
161
+ }
162
+
163
+ const reader = body.getReader();
143
164
  const chunks = [];
144
165
  let bytes = 0;
145
166
 
146
167
  try {
147
- for await (const chunk of body) {
168
+ let readResult = await reader.read();
169
+ while (!readResult.done) {
170
+ if (body[PropertySymbol.error]) {
171
+ throw body[PropertySymbol.error];
172
+ }
173
+ const chunk = readResult.value;
148
174
  bytes += chunk.length;
149
175
  chunks.push(chunk);
176
+ readResult = await reader.read();
150
177
  }
151
178
  } catch (error) {
152
179
  if (error instanceof DOMException) {
@@ -158,16 +185,6 @@ export default class FetchBodyUtility {
158
185
  );
159
186
  }
160
187
 
161
- if (
162
- (<Stream.Readable>body).readableEnded === false ||
163
- (<Stream.Readable>body)['_readableState']?.ended === false
164
- ) {
165
- throw new DOMException(
166
- `Premature close of server response.`,
167
- DOMExceptionNameEnum.invalidStateError
168
- );
169
- }
170
-
171
188
  try {
172
189
  if (typeof chunks[0] === 'string') {
173
190
  return Buffer.from(chunks.join(''));
@@ -113,6 +113,7 @@ import ProcessingInstruction from '../nodes/processing-instruction/ProcessingIns
113
113
  import RequestInfo from '../fetch/types/IRequestInfo.js';
114
114
  import FileList from '../nodes/html-input-element/FileList.js';
115
115
  import Stream from 'stream';
116
+ import { ReadableStream } from 'stream/web';
116
117
  import FormData from '../form-data/FormData.js';
117
118
  import AbortController from '../fetch/AbortController.js';
118
119
  import AbortSignal from '../fetch/AbortSignal.js';
@@ -383,7 +384,7 @@ export default class BrowserWindow extends EventTarget implements IBrowserWindow
383
384
  };
384
385
  public readonly XMLHttpRequestUpload = XMLHttpRequestUpload;
385
386
  public readonly XMLHttpRequestEventTarget = XMLHttpRequestEventTarget;
386
- public readonly ReadableStream = Stream.Readable;
387
+ public readonly ReadableStream = ReadableStream;
387
388
  public readonly WritableStream = Stream.Writable;
388
389
  public readonly TransformStream = Stream.Transform;
389
390
  public readonly AbortController = AbortController;
@@ -116,6 +116,7 @@ import ProcessingInstruction from '../nodes/processing-instruction/ProcessingIns
116
116
  import RequestInfo from '../fetch/types/IRequestInfo.js';
117
117
  import FileList from '../nodes/html-input-element/FileList.js';
118
118
  import Stream from 'stream';
119
+ import { ReadableStream } from 'stream/web';
119
120
  import { webcrypto } from 'crypto';
120
121
  import FormData from '../form-data/FormData.js';
121
122
  import AbortController from '../fetch/AbortController.js';
@@ -361,7 +362,7 @@ export default interface IBrowserWindow extends IEventTarget, INodeJSGlobal {
361
362
  readonly XMLHttpRequestUpload: typeof XMLHttpRequestUpload;
362
363
  readonly XMLHttpRequestEventTarget: typeof XMLHttpRequestEventTarget;
363
364
  readonly FileList: typeof FileList;
364
- readonly ReadableStream: typeof Stream.Readable;
365
+ readonly ReadableStream: typeof ReadableStream;
365
366
  readonly WritableStream: typeof Stream.Writable;
366
367
  readonly TransformStream: typeof Stream.Transform;
367
368
  readonly FormData: typeof FormData;