happy-dom 2.50.0 → 2.51.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 (104) hide show
  1. package/lib/async-task-manager/AsyncTaskManager.d.ts +65 -0
  2. package/lib/{window → async-task-manager}/AsyncTaskManager.js +52 -55
  3. package/lib/async-task-manager/AsyncTaskManager.js.map +1 -0
  4. package/lib/fetch/FetchHandler.d.ts +17 -0
  5. package/lib/fetch/FetchHandler.js +59 -0
  6. package/lib/fetch/FetchHandler.js.map +1 -0
  7. package/lib/fetch/Headers.d.ts +7 -0
  8. package/lib/fetch/Headers.js +53 -0
  9. package/lib/fetch/Headers.js.map +1 -0
  10. package/lib/fetch/IAbortSignal.d.ts +16 -0
  11. package/lib/fetch/IAbortSignal.js +4 -0
  12. package/lib/fetch/IAbortSignal.js.map +1 -0
  13. package/lib/fetch/IBody.d.ts +17 -0
  14. package/lib/{window/IFetchOptions.js → fetch/IBody.js} +1 -1
  15. package/lib/fetch/IBody.js.map +1 -0
  16. package/lib/fetch/IHeaders.d.ts +18 -0
  17. package/lib/fetch/IHeaders.js +3 -0
  18. package/lib/fetch/IHeaders.js.map +1 -0
  19. package/lib/fetch/IHeadersInit.d.ts +5 -0
  20. package/lib/fetch/IHeadersInit.js +3 -0
  21. package/lib/fetch/IHeadersInit.js.map +1 -0
  22. package/lib/fetch/IRequest.d.ts +18 -0
  23. package/lib/fetch/IRequest.js +3 -0
  24. package/lib/fetch/IRequest.js.map +1 -0
  25. package/lib/fetch/IRequestInit.d.ts +15 -0
  26. package/lib/fetch/IRequestInit.js +3 -0
  27. package/lib/fetch/IRequestInit.js.map +1 -0
  28. package/lib/fetch/IResponse.d.ts +20 -0
  29. package/lib/{window → fetch}/IResponse.js +0 -0
  30. package/lib/{window → fetch}/IResponse.js.map +1 -1
  31. package/lib/fetch/IResponseInit.d.ts +9 -0
  32. package/lib/fetch/IResponseInit.js +3 -0
  33. package/lib/fetch/IResponseInit.js.map +1 -0
  34. package/lib/fetch/Request.d.ts +69 -0
  35. package/lib/fetch/Request.js +179 -0
  36. package/lib/fetch/Request.js.map +1 -0
  37. package/lib/fetch/ResourceFetchHandler.d.ts +22 -0
  38. package/lib/fetch/{ResourceFetcher.js → ResourceFetchHandler.js} +19 -26
  39. package/lib/fetch/ResourceFetchHandler.js.map +1 -0
  40. package/lib/fetch/Response.d.ts +69 -0
  41. package/lib/fetch/Response.js +179 -0
  42. package/lib/fetch/Response.js.map +1 -0
  43. package/lib/file/Blob.d.ts +8 -1
  44. package/lib/file/Blob.js +48 -0
  45. package/lib/file/Blob.js.map +1 -1
  46. package/lib/file/IBlob.d.ts +10 -0
  47. package/lib/file/IBlob.js +3 -0
  48. package/lib/file/IBlob.js.map +1 -0
  49. package/lib/form-data/IFormData.d.ts +30 -0
  50. package/lib/form-data/IFormData.js +3 -0
  51. package/lib/form-data/IFormData.js.map +1 -0
  52. package/lib/index.d.ts +3 -5
  53. package/lib/index.js +3 -3
  54. package/lib/index.js.map +1 -1
  55. package/lib/nodes/html-link-element/HTMLLinkElement.js +3 -3
  56. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  57. package/lib/nodes/html-script-element/ScriptUtility.js +3 -9
  58. package/lib/nodes/html-script-element/ScriptUtility.js.map +1 -1
  59. package/lib/window/IWindow.d.ts +20 -16
  60. package/lib/window/Window.d.ts +45 -10
  61. package/lib/window/Window.js +72 -66
  62. package/lib/window/Window.js.map +1 -1
  63. package/package.json +3 -2
  64. package/src/async-task-manager/AsyncTaskManager.ts +128 -0
  65. package/src/fetch/FetchHandler.ts +54 -0
  66. package/src/fetch/Headers.ts +7 -0
  67. package/src/fetch/IAbortSignal.ts +34 -0
  68. package/src/fetch/IBody.ts +18 -0
  69. package/src/fetch/IHeaders.ts +18 -0
  70. package/src/fetch/IHeadersInit.ts +5 -0
  71. package/src/fetch/IRequest.ts +59 -0
  72. package/src/fetch/IRequestInit.ts +41 -0
  73. package/src/fetch/IResponse.ts +22 -0
  74. package/src/fetch/IResponseInit.ts +10 -0
  75. package/src/fetch/Request.ts +149 -0
  76. package/src/fetch/ResourceFetchHandler.ts +47 -0
  77. package/src/fetch/Response.ts +149 -0
  78. package/src/file/Blob.ts +12 -1
  79. package/src/file/IBlob.ts +10 -0
  80. package/src/form-data/IFormData.ts +33 -0
  81. package/src/index.ts +2 -6
  82. package/src/nodes/html-link-element/HTMLLinkElement.ts +3 -3
  83. package/src/nodes/html-script-element/ScriptUtility.ts +3 -9
  84. package/src/window/IWindow.ts +14 -16
  85. package/src/window/Window.ts +68 -67
  86. package/lib/fetch/ResourceFetcher.d.ts +0 -30
  87. package/lib/fetch/ResourceFetcher.js.map +0 -1
  88. package/lib/url-search-params/URLSearchParams.d.ts +0 -87
  89. package/lib/url-search-params/URLSearchParams.js +0 -196
  90. package/lib/url-search-params/URLSearchParams.js.map +0 -1
  91. package/lib/window/AsyncTaskManager.d.ts +0 -54
  92. package/lib/window/AsyncTaskManager.js.map +0 -1
  93. package/lib/window/AsyncTaskTypeEnum.d.ts +0 -5
  94. package/lib/window/AsyncTaskTypeEnum.js +0 -9
  95. package/lib/window/AsyncTaskTypeEnum.js.map +0 -1
  96. package/lib/window/IFetchOptions.d.ts +0 -13
  97. package/lib/window/IFetchOptions.js.map +0 -1
  98. package/lib/window/IResponse.d.ts +0 -32
  99. package/src/fetch/ResourceFetcher.ts +0 -55
  100. package/src/url-search-params/URLSearchParams.ts +0 -198
  101. package/src/window/AsyncTaskManager.ts +0 -127
  102. package/src/window/AsyncTaskTypeEnum.ts +0 -5
  103. package/src/window/IFetchOptions.ts +0 -11
  104. package/src/window/IResponse.ts +0 -34
@@ -0,0 +1,47 @@
1
+ import RelativeURL from '../location/RelativeURL';
2
+ import DOMException from '../exception/DOMException';
3
+ import IDocument from '../nodes/document/IDocument';
4
+
5
+ /**
6
+ * Helper class for performing fetch of resources.
7
+ */
8
+ export default class ResourceFetchHandler {
9
+ /**
10
+ * Returns resource data asynchonously.
11
+ *
12
+ * @param document Document.
13
+ * @param url URL.
14
+ * @returns Response.
15
+ */
16
+ public static async fetch(document: IDocument, url: string): Promise<string> {
17
+ const response = await document.defaultView.fetch(url);
18
+ if (!response.ok) {
19
+ throw new DOMException(
20
+ `Failed to perform request to "${url}". Status code: ${response.status}`
21
+ );
22
+ }
23
+ return await response.text();
24
+ }
25
+
26
+ /**
27
+ * Returns resource data synchonously.
28
+ *
29
+ * @param document Document.
30
+ * @param url URL.
31
+ * @returns Response.
32
+ */
33
+ public static fetchSync(document: IDocument, url: string): string {
34
+ // We want to only load SyncRequest when it is needed to improve performance and not have direct dependencies to server side packages.
35
+ const syncRequest = require('sync-request');
36
+ const absoluteURL = RelativeURL.getAbsoluteURL(document.defaultView.location, url);
37
+ const response = syncRequest('GET', absoluteURL);
38
+
39
+ if (response.isError()) {
40
+ throw new DOMException(
41
+ `Failed to perform request to "${absoluteURL}". Status code: ${response.statusCode}`
42
+ );
43
+ }
44
+
45
+ return response.getBody();
46
+ }
47
+ }
@@ -0,0 +1,149 @@
1
+ import IResponse from './IResponse';
2
+ import IBlob from '../file/IBlob';
3
+ import IDocument from '../nodes/document/IDocument';
4
+ import * as NodeFetch from 'node-fetch';
5
+
6
+ /**
7
+ * Fetch response.
8
+ */
9
+ export default class Response extends NodeFetch.Response implements IResponse {
10
+ public static _ownerDocument: IDocument = null;
11
+
12
+ /**
13
+ * Returns array buffer.
14
+ *
15
+ * @returns Array buffer.
16
+ */
17
+ public arrayBuffer(): Promise<ArrayBuffer> {
18
+ return new Promise((resolve, reject) => {
19
+ const taskID = this._handlePromiseStart();
20
+ super
21
+ .arrayBuffer()
22
+ .then(this._handlePromiseEnd.bind(this, resolve, reject, taskID))
23
+ .catch(this._handlePromiseError.bind(this, reject));
24
+ });
25
+ }
26
+
27
+ /**
28
+ * Returns blob.
29
+ *
30
+ * @returns Blob.
31
+ */
32
+ public blob(): Promise<IBlob> {
33
+ return new Promise((resolve, reject) => {
34
+ const taskID = this._handlePromiseStart();
35
+ super
36
+ .blob()
37
+ .then(this._handlePromiseEnd.bind(this, resolve, reject, taskID))
38
+ .catch(this._handlePromiseError.bind(this, reject));
39
+ });
40
+ }
41
+
42
+ /**
43
+ * Returns buffer.
44
+ *
45
+ * @returns Buffer.
46
+ */
47
+ public buffer(): Promise<Buffer> {
48
+ return new Promise((resolve, reject) => {
49
+ const taskID = this._handlePromiseStart();
50
+ super
51
+ .buffer()
52
+ .then(this._handlePromiseEnd.bind(this, resolve, reject, taskID))
53
+ .catch(this._handlePromiseError.bind(this, reject));
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Returns json.
59
+ *
60
+ * @returns JSON.
61
+ */
62
+ public json(): Promise<unknown> {
63
+ return new Promise((resolve, reject) => {
64
+ const taskID = this._handlePromiseStart();
65
+ super
66
+ .json()
67
+ .then(this._handlePromiseEnd.bind(this, resolve, reject, taskID))
68
+ .catch(this._handlePromiseError.bind(this, reject));
69
+ });
70
+ }
71
+
72
+ /**
73
+ * Returns json.
74
+ *
75
+ * @returns JSON.
76
+ */
77
+ public text(): Promise<string> {
78
+ return new Promise((resolve, reject) => {
79
+ const taskID = this._handlePromiseStart();
80
+ super
81
+ .text()
82
+ .then(this._handlePromiseEnd.bind(this, resolve, reject, taskID))
83
+ .catch(this._handlePromiseError.bind(this, reject));
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Returns json.
89
+ *
90
+ * @returns JSON.
91
+ */
92
+ public textConverted(): Promise<string> {
93
+ return new Promise((resolve, reject) => {
94
+ const taskID = this._handlePromiseStart();
95
+ super
96
+ .textConverted()
97
+ .then(this._handlePromiseEnd.bind(this, resolve, reject, taskID))
98
+ .catch(this._handlePromiseError.bind(this, reject));
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Handles promise start.
104
+ *
105
+ * @returns Task ID.
106
+ */
107
+ private _handlePromiseStart(): number {
108
+ const taskManager = (<typeof Response>this.constructor)._ownerDocument.defaultView.happyDOM
109
+ .asyncTaskManager;
110
+ return taskManager.startTask();
111
+ }
112
+
113
+ /**
114
+ * Handles promise end.
115
+ *
116
+ * @param resolve Resolve.
117
+ * @param reject Reject.
118
+ * @param taskID Task ID.
119
+ * @param response Response.
120
+ */
121
+ private _handlePromiseEnd(
122
+ resolve: (response: unknown) => void,
123
+ reject: (error: Error) => void,
124
+ taskID: number,
125
+ response: unknown
126
+ ): void {
127
+ const taskManager = (<typeof Response>this.constructor)._ownerDocument.defaultView.happyDOM
128
+ .asyncTaskManager;
129
+ if (taskManager.getTaskCount() === 0) {
130
+ reject(new Error('Failed to complete fetch request. Task was canceled.'));
131
+ } else {
132
+ resolve(response);
133
+ taskManager.endTask(taskID);
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Handles promise error.
139
+ *
140
+ * @param error
141
+ * @param reject
142
+ */
143
+ private _handlePromiseError(reject: (error: Error) => void, error: Error): void {
144
+ const taskManager = (<typeof Response>this.constructor)._ownerDocument.defaultView.happyDOM
145
+ .asyncTaskManager;
146
+ reject(error);
147
+ taskManager.cancelAll(error);
148
+ }
149
+ }
package/src/file/Blob.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import IBlob from './IBlob';
2
+
1
3
  /**
2
4
  * Reference:
3
5
  * https://developer.mozilla.org/en-US/docs/Web/API/Blob.
@@ -5,7 +7,7 @@
5
7
  * Based on:
6
8
  * https://github.com/jsdom/jsdom/blob/master/lib/jsdom/living/file-api/Blob-impl.js (MIT licensed).
7
9
  */
8
- export default class Blob {
10
+ export default class Blob implements IBlob {
9
11
  public readonly _buffer: Buffer = null;
10
12
  public readonly type: string = '';
11
13
 
@@ -108,6 +110,15 @@ export default class Blob {
108
110
  return blob;
109
111
  }
110
112
 
113
+ /**
114
+ * Returns a Promise that resolves to a text.
115
+ *
116
+ * @returns Text.
117
+ */
118
+ public async text(): Promise<string> {
119
+ return this._buffer.toString();
120
+ }
121
+
111
122
  /**
112
123
  * Closes the blob.
113
124
  *
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Reference:
3
+ * https://developer.mozilla.org/en-US/docs/Web/API/Blob.
4
+ */
5
+ export default interface IBlob {
6
+ readonly type: string;
7
+ readonly size: number;
8
+ slice(start?: number, end?: number): IBlob;
9
+ text(): Promise<string>;
10
+ }
@@ -0,0 +1,33 @@
1
+ import Stream from 'stream';
2
+ import HTTP from 'http';
3
+
4
+ interface IHeaders {
5
+ [key: string]: unknown;
6
+ }
7
+
8
+ interface IAppendOptions {
9
+ header?: string | IHeaders;
10
+ knownLength?: number;
11
+ filename?: string;
12
+ filepath?: string;
13
+ contentType?: string;
14
+ }
15
+
16
+ interface ISubmitOptions extends HTTP.RequestOptions {
17
+ protocol?: 'https:' | 'http:';
18
+ }
19
+
20
+ export default interface IFormData extends Stream.Readable {
21
+ append(key: string, value: unknown, options?: IAppendOptions | string): void;
22
+ getHeaders(userHeaders?: IHeaders): { [key: string]: IHeaders };
23
+ submit(
24
+ params: string | ISubmitOptions,
25
+ callback?: (error: Error | null, response: HTTP.IncomingMessage) => void
26
+ ): HTTP.ClientRequest;
27
+ getBuffer(): Buffer;
28
+ setBoundary(boundary: string): void;
29
+ getBoundary(): string;
30
+ getLength(callback: (err: Error | null, length: number) => void): void;
31
+ getLengthSync(): number;
32
+ hasKnownLength(): boolean;
33
+ }
package/src/index.ts CHANGED
@@ -14,9 +14,7 @@ import DOMException from './exception/DOMException';
14
14
  import History from './history/History';
15
15
  import CSSStyleDeclaration from './css/CSSStyleDeclaration';
16
16
  import Screen from './screen/Screen';
17
- import AsyncTaskManager from './window/AsyncTaskManager';
18
- import IResponse from './window/IResponse';
19
- import IFetchOptions from './window/IFetchOptions';
17
+ import AsyncTaskManager from './async-task-manager/AsyncTaskManager';
20
18
  import NodeFilter from './tree-walker/NodeFilter';
21
19
  import Event from './event/Event';
22
20
  import EventTarget from './event/EventTarget';
@@ -101,7 +99,7 @@ import XMLParser from './xml-parser/XMLParser';
101
99
  import XMLSerializer from './xml-serializer/XMLSerializer';
102
100
  import CSSStyleSheet from './css/CSSStyleSheet';
103
101
  import Storage from './storage/Storage';
104
- import URLSearchParams from './url-search-params/URLSearchParams';
102
+ import { URLSearchParams } from 'url';
105
103
  import Selection from './selection/Selection';
106
104
 
107
105
  export {
@@ -123,8 +121,6 @@ export {
123
121
  CSSStyleDeclaration,
124
122
  Screen,
125
123
  AsyncTaskManager,
126
- IResponse,
127
- IFetchOptions,
128
124
  NodeFilter,
129
125
  Event,
130
126
  EventTarget,
@@ -1,6 +1,6 @@
1
1
  import Attr from '../../attribute/Attr';
2
2
  import CSSStyleSheet from '../../css/CSSStyleSheet';
3
- import ResourceFetcher from '../../fetch/ResourceFetcher';
3
+ import ResourceFetchHandler from '../../fetch/ResourceFetchHandler';
4
4
  import HTMLElement from '../html-element/HTMLElement';
5
5
  import Document from '../document/Document';
6
6
  import IHTMLLinkElement from './IHTMLLinkElement';
@@ -199,7 +199,7 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
199
199
  this.isConnected
200
200
  ) {
201
201
  (<Document>this.ownerDocument)._readyStateManager.startTask();
202
- ResourceFetcher.fetch({ window: this.ownerDocument.defaultView, url: href })
202
+ ResourceFetchHandler.fetch(this.ownerDocument, href)
203
203
  .then((code) => {
204
204
  const styleSheet = new CSSStyleSheet();
205
205
  styleSheet.replaceSync(code);
@@ -245,7 +245,7 @@ export default class HTMLLinkElement extends HTMLElement implements IHTMLLinkEle
245
245
 
246
246
  if (href !== null && rel && rel.toLowerCase() === 'stylesheet') {
247
247
  (<Document>this.ownerDocument)._readyStateManager.startTask();
248
- ResourceFetcher.fetch({ window: this.ownerDocument.defaultView, url: href })
248
+ ResourceFetchHandler.fetch(this.ownerDocument, href)
249
249
  .then((code) => {
250
250
  const styleSheet = new CSSStyleSheet();
251
251
  styleSheet.replaceSync(code);
@@ -1,7 +1,7 @@
1
1
  import { Document } from '../..';
2
2
  import Event from '../../event/Event';
3
3
  import ErrorEvent from '../../event/events/ErrorEvent';
4
- import ResourceFetcher from '../../fetch/ResourceFetcher';
4
+ import ResourceFetchHandler from '../../fetch/ResourceFetchHandler';
5
5
  import HTMLScriptElement from './HTMLScriptElement';
6
6
 
7
7
  /**
@@ -22,10 +22,7 @@ export default class ScriptUtility {
22
22
  let code = null;
23
23
  (<Document>element.ownerDocument)._readyStateManager.startTask();
24
24
  try {
25
- code = await ResourceFetcher.fetch({
26
- window: element.ownerDocument.defaultView,
27
- url: src
28
- });
25
+ code = await ResourceFetchHandler.fetch(element.ownerDocument, src);
29
26
  } catch (error) {
30
27
  element.dispatchEvent(
31
28
  new ErrorEvent('error', {
@@ -54,10 +51,7 @@ export default class ScriptUtility {
54
51
  } else {
55
52
  let code = null;
56
53
  try {
57
- code = ResourceFetcher.fetchSync({
58
- window: element.ownerDocument.defaultView,
59
- url: src
60
- });
54
+ code = ResourceFetchHandler.fetchSync(element.ownerDocument, src);
61
55
  } catch (error) {
62
56
  element.dispatchEvent(
63
57
  new ErrorEvent('error', {
@@ -60,13 +60,15 @@ import UIEvent from '../event/UIEvent';
60
60
  import ErrorEvent from '../event/events/ErrorEvent';
61
61
  import StorageEvent from '../event/events/StorageEvent';
62
62
  import Screen from '../screen/Screen';
63
- import AsyncTaskManager from './AsyncTaskManager';
64
- import IResponse from './IResponse';
63
+ import AsyncTaskManager from '../async-task-manager/AsyncTaskManager';
64
+ import IResponse from '../fetch/IResponse';
65
+ import IResponseInit from '../fetch/IResponseInit';
66
+ import IRequest from '../fetch/IRequest';
67
+ import IRequestInit from '../fetch/IRequestInit';
68
+ import IHeaders from '../fetch/IHeaders';
65
69
  import Storage from '../storage/Storage';
66
- import IFetchOptions from './IFetchOptions';
67
70
  import NodeFilter from '../tree-walker/NodeFilter';
68
71
  import Window from './Window';
69
- import URLSearchParams from '../url-search-params/URLSearchParams';
70
72
  import HTMLCollection from '../nodes/element/HTMLCollection';
71
73
  import NodeList from '../nodes/node/NodeList';
72
74
  import Selection from '../selection/Selection';
@@ -76,6 +78,7 @@ import MimeType from '../navigator/MimeType';
76
78
  import MimeTypeArray from '../navigator/MimeTypeArray';
77
79
  import Plugin from '../navigator/Plugin';
78
80
  import PluginArray from '../navigator/PluginArray';
81
+ import { URLSearchParams } from 'url';
79
82
 
80
83
  /**
81
84
  * Window.
@@ -141,7 +144,9 @@ export default interface IWindow extends IEventTarget {
141
144
  readonly Location: typeof Location;
142
145
  readonly CustomElementRegistry: typeof CustomElementRegistry;
143
146
  readonly Window: typeof Window;
144
- readonly Headers: typeof Map;
147
+ readonly Headers: { new (init?: string[][] | Record<string, string> | IHeaders): IHeaders };
148
+ readonly Request: { new (input: string | IRequest, init?: IRequestInit): IRequest };
149
+ readonly Response: { new (body?: unknown | null, init?: IResponseInit): IResponse };
145
150
  readonly XMLSerializer: typeof XMLSerializer;
146
151
  readonly ResizeObserver: typeof ResizeObserver;
147
152
  readonly CSSStyleSheet: typeof CSSStyleSheet;
@@ -221,7 +226,6 @@ export default interface IWindow extends IEventTarget {
221
226
  /**
222
227
  * Sets a timer which executes a function once the timer expires.
223
228
  *
224
- * @override
225
229
  * @param callback Function to be executed.
226
230
  * @param [delay=0] Delay in ms.
227
231
  * @returns Timeout ID.
@@ -231,7 +235,6 @@ export default interface IWindow extends IEventTarget {
231
235
  /**
232
236
  * Cancels a timeout previously established by calling setTimeout().
233
237
  *
234
- * @override
235
238
  * @param id ID of the timeout.
236
239
  */
237
240
  clearTimeout(id: NodeJS.Timeout): void;
@@ -239,7 +242,6 @@ export default interface IWindow extends IEventTarget {
239
242
  /**
240
243
  * Calls a function with a fixed time delay between each call.
241
244
  *
242
- * @override
243
245
  * @param callback Function to be executed.
244
246
  * @param [delay=0] Delay in ms.
245
247
  * @returns Interval ID.
@@ -249,7 +251,6 @@ export default interface IWindow extends IEventTarget {
249
251
  /**
250
252
  * Cancels a timed repeating action which was previously established by a call to setInterval().
251
253
  *
252
- * @override
253
254
  * @param id ID of the interval.
254
255
  */
255
256
  clearInterval(id: NodeJS.Timeout): void;
@@ -257,7 +258,6 @@ export default interface IWindow extends IEventTarget {
257
258
  /**
258
259
  * Mock animation frames with timeouts.
259
260
  *
260
- * @override
261
261
  * @param {Function} callback Callback.
262
262
  * @returns {NodeJS.Timeout} Timeout ID.
263
263
  */
@@ -266,18 +266,16 @@ export default interface IWindow extends IEventTarget {
266
266
  /**
267
267
  * Mock animation frames with timeouts.
268
268
  *
269
- * @override
270
269
  * @param {NodeJS.Timeout} id Timeout ID.
271
270
  */
272
271
  cancelAnimationFrame(id: NodeJS.Timeout): void;
273
272
 
274
273
  /**
275
- * Provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.
274
+ * This method provides an easy, logical way to fetch resources asynchronously across the network.
276
275
  *
277
- * @override
278
- * @param url URL to resource.
279
- * @param [options] Options.
276
+ * @param url URL.
277
+ * @param [init] Init.
280
278
  * @returns Promise.
281
279
  */
282
- fetch(url: string, options?: IFetchOptions): Promise<IResponse>;
280
+ fetch(url: string, init?: IRequestInit): Promise<IResponse>;
283
281
  }
@@ -63,14 +63,15 @@ import UIEvent from '../event/UIEvent';
63
63
  import ErrorEvent from '../event/events/ErrorEvent';
64
64
  import StorageEvent from '../event/events/StorageEvent';
65
65
  import Screen from '../screen/Screen';
66
- import AsyncTaskManager from './AsyncTaskManager';
67
- import IResponse from './IResponse';
68
- import AsyncTaskTypeEnum from './AsyncTaskTypeEnum';
69
- import RelativeURL from '../location/RelativeURL';
66
+ import AsyncTaskManager from '../async-task-manager/AsyncTaskManager';
67
+ import IResponse from '../fetch/IResponse';
68
+ import IResponseInit from '../fetch/IResponseInit';
69
+ import IRequest from '../fetch/IRequest';
70
+ import IRequestInit from '../fetch/IRequestInit';
71
+ import IHeaders from '../fetch/IHeaders';
72
+ import IHeadersInit from '../fetch/IHeadersInit';
70
73
  import Storage from '../storage/Storage';
71
- import IFetchOptions from './IFetchOptions';
72
74
  import IWindow from './IWindow';
73
- import URLSearchParams from '../url-search-params/URLSearchParams';
74
75
  import HTMLCollection from '../nodes/element/HTMLCollection';
75
76
  import NodeList from '../nodes/node/NodeList';
76
77
  import MediaQueryList from '../match-media/MediaQueryList';
@@ -81,8 +82,8 @@ import MimeType from '../navigator/MimeType';
81
82
  import MimeTypeArray from '../navigator/MimeTypeArray';
82
83
  import Plugin from '../navigator/Plugin';
83
84
  import PluginArray from '../navigator/PluginArray';
84
-
85
- const FETCH_RESPONSE_TYPE_METHODS = ['blob', 'json', 'text'];
85
+ import FetchHandler from '../fetch/FetchHandler';
86
+ import { URLSearchParams } from 'url';
86
87
 
87
88
  /**
88
89
  * Handles the Window.
@@ -94,7 +95,7 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
94
95
  return await this.happyDOM.asyncTaskManager.whenComplete();
95
96
  },
96
97
  cancelAsync: (): void => {
97
- this.happyDOM.asyncTaskManager.cancelAllTasks();
98
+ this.happyDOM.asyncTaskManager.cancelAll();
98
99
  },
99
100
  asyncTaskManager: new AsyncTaskManager()
100
101
  };
@@ -152,7 +153,6 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
152
153
  public readonly Location = Location;
153
154
  public readonly CustomElementRegistry = CustomElementRegistry;
154
155
  public readonly Window = Window;
155
- public readonly Headers = Map;
156
156
  public readonly XMLSerializer = XMLSerializer;
157
157
  public readonly ResizeObserver = ResizeObserver;
158
158
  public readonly CSSStyleSheet = CSSStyleSheet;
@@ -200,7 +200,6 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
200
200
  public readonly performance = PerfHooks.performance;
201
201
 
202
202
  // Node.js Globals
203
- public Array = global ? global.Array : null;
204
203
  public ArrayBuffer = global ? global.ArrayBuffer : null;
205
204
  public Boolean = global ? global.Boolean : null;
206
205
  public Buffer = null;
@@ -262,6 +261,7 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
262
261
 
263
262
  // Private properties
264
263
  private _objectClass: typeof globalThis.Object = null;
264
+ private _arrayClass: typeof globalThis.Array = null;
265
265
  private _functionClass: typeof globalThis.Function = null;
266
266
 
267
267
  /**
@@ -315,6 +315,21 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
315
315
  return this._objectClass;
316
316
  }
317
317
 
318
+ /**
319
+ * Returns Array class.
320
+ *
321
+ * @returns Array class.
322
+ */
323
+ public get Array(): typeof globalThis.Array {
324
+ if (this._arrayClass) {
325
+ return this._arrayClass;
326
+ }
327
+ // When inside a VM global.Object is not the same as [].constructor
328
+ // We will therefore run the code inside the VM to get the real constructor
329
+ this._arrayClass = <typeof globalThis.Array>this.eval('[].constructor');
330
+ return this._arrayClass;
331
+ }
332
+
318
333
  /**
319
334
  * Returns Function class.
320
335
  *
@@ -339,6 +354,43 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
339
354
  return new CSS();
340
355
  }
341
356
 
357
+ /**
358
+ * Returns Headers class.
359
+ *
360
+ * @returns Headers.
361
+ */
362
+ public get Headers(): {
363
+ new (init?: IHeadersInit): IHeaders;
364
+ } {
365
+ return require('../fetch/Headers').default;
366
+ }
367
+
368
+ /**
369
+ * Returns Request class.
370
+ *
371
+ * @returns Request.
372
+ */
373
+ public get Request(): {
374
+ new (input: string | { href: string } | IRequest, init?: IRequestInit): IRequest;
375
+ } {
376
+ const Request = require('../fetch/Request').default;
377
+ Request._ownerDocument = Request._ownerDocument || this.document;
378
+ return Request;
379
+ }
380
+
381
+ /**
382
+ * Returns Response class.
383
+ *
384
+ * @returns Response.
385
+ */
386
+ public get Response(): {
387
+ new (body?: NodeJS.ReadableStream | null, init?: IResponseInit): IResponse;
388
+ } {
389
+ const Response = require('../fetch/Response').default;
390
+ Response._ownerDocument = Response._ownerDocument || this.document;
391
+ return Response;
392
+ }
393
+
342
394
  /**
343
395
  * Evaluates code.
344
396
  *
@@ -509,65 +561,14 @@ export default class Window extends EventTarget implements IWindow, NodeJS.Globa
509
561
  }
510
562
 
511
563
  /**
512
- * Provides a global fetch() method that provides an easy, logical way to fetch resources asynchronously across the network.
564
+ * This method provides an easy, logical way to fetch resources asynchronously across the network.
513
565
  *
514
566
  * @override
515
- * @param url URL to resource.
516
- * @param [options] Options.
567
+ * @param url URL.
568
+ * @param [init] Init.
517
569
  * @returns Promise.
518
570
  */
519
- public async fetch(url: string, options?: IFetchOptions): Promise<IResponse> {
520
- return new Promise((resolve, reject) => {
521
- let fetch = null;
522
-
523
- try {
524
- fetch = require('node-fetch');
525
- } catch (error) {
526
- throw new Error('Failed to perform fetch. "node-fetch" could not be loaded.');
527
- }
528
-
529
- this.happyDOM.asyncTaskManager.startTask(AsyncTaskTypeEnum.fetch);
530
-
531
- fetch(RelativeURL.getAbsoluteURL(this.location, url), options)
532
- .then((response) => {
533
- if (this.happyDOM.asyncTaskManager.getRunningCount(AsyncTaskTypeEnum.fetch) === 0) {
534
- reject(new Error('Failed to complete fetch request. Task was canceled.'));
535
- } else {
536
- for (const methodName of FETCH_RESPONSE_TYPE_METHODS) {
537
- const asyncMethod = response[methodName];
538
- response[methodName] = () => {
539
- return new Promise((resolve, reject) => {
540
- this.happyDOM.asyncTaskManager.startTask(AsyncTaskTypeEnum.fetch);
541
-
542
- asyncMethod
543
- .call(response)
544
- .then((response) => {
545
- if (
546
- this.happyDOM.asyncTaskManager.getRunningCount(AsyncTaskTypeEnum.fetch) ===
547
- 0
548
- ) {
549
- reject(new Error('Failed to complete fetch request. Task was canceled.'));
550
- } else {
551
- resolve(response);
552
- this.happyDOM.asyncTaskManager.endTask(AsyncTaskTypeEnum.fetch);
553
- }
554
- })
555
- .catch((error) => {
556
- reject(error);
557
- this.happyDOM.asyncTaskManager.endTask(AsyncTaskTypeEnum.fetch, error);
558
- });
559
- });
560
- };
561
- }
562
-
563
- resolve(response);
564
- this.happyDOM.asyncTaskManager.endTask(AsyncTaskTypeEnum.fetch);
565
- }
566
- })
567
- .catch((error) => {
568
- reject(error);
569
- this.happyDOM.asyncTaskManager.endTask(AsyncTaskTypeEnum.fetch, error);
570
- });
571
- });
571
+ public async fetch(url: string, init?: IRequestInit): Promise<IResponse> {
572
+ return await FetchHandler.fetch(this.document, url, init);
572
573
  }
573
574
  }
@@ -1,30 +0,0 @@
1
- import Window from '../window/Window';
2
- /**
3
- * Helper class for performing an asynchonous or synchrounous request to a resource.
4
- */
5
- export default class ResourceFetcher {
6
- /**
7
- * Returns resource data asynchonously.
8
- *
9
- * @param options Options.
10
- * @param options.window Location.
11
- * @param options.url URL.
12
- * @returns Response.
13
- */
14
- static fetch(options: {
15
- window: Window;
16
- url: string;
17
- }): Promise<string>;
18
- /**
19
- * Returns resource data synchonously.
20
- *
21
- * @param options Options.
22
- * @param options.window Location.
23
- * @param options.url URL.
24
- * @returns Response.
25
- */
26
- static fetchSync(options: {
27
- window: Window;
28
- url: string;
29
- }): string;
30
- }