happy-dom 2.49.2 → 2.51.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.

Potentially problematic release.


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

Files changed (140) hide show
  1. package/.eslintrc.js +7 -6
  2. package/.prettierrc.js +2 -1
  3. package/lib/async-task-manager/AsyncTaskManager.d.ts +65 -0
  4. package/lib/{window → async-task-manager}/AsyncTaskManager.js +52 -55
  5. package/lib/async-task-manager/AsyncTaskManager.js.map +1 -0
  6. package/lib/cookie/CookieUtility.js +1 -4
  7. package/lib/cookie/CookieUtility.js.map +1 -1
  8. package/lib/custom-element/CustomElementRegistry.js.map +1 -1
  9. package/lib/dom-token-list/DOMTokenList.d.ts +0 -4
  10. package/lib/dom-token-list/DOMTokenList.js +0 -4
  11. package/lib/dom-token-list/DOMTokenList.js.map +1 -1
  12. package/lib/fetch/FetchHandler.d.ts +17 -0
  13. package/lib/fetch/FetchHandler.js +59 -0
  14. package/lib/fetch/FetchHandler.js.map +1 -0
  15. package/lib/fetch/Headers.d.ts +7 -0
  16. package/lib/fetch/Headers.js +53 -0
  17. package/lib/fetch/Headers.js.map +1 -0
  18. package/lib/fetch/IAbortSignal.d.ts +16 -0
  19. package/lib/fetch/IAbortSignal.js +4 -0
  20. package/lib/fetch/IAbortSignal.js.map +1 -0
  21. package/lib/fetch/IBody.d.ts +17 -0
  22. package/lib/{window/IFetchOptions.js → fetch/IBody.js} +1 -1
  23. package/lib/fetch/IBody.js.map +1 -0
  24. package/lib/fetch/IHeaders.d.ts +18 -0
  25. package/lib/fetch/IHeaders.js +3 -0
  26. package/lib/fetch/IHeaders.js.map +1 -0
  27. package/lib/fetch/IHeadersInit.d.ts +5 -0
  28. package/lib/fetch/IHeadersInit.js +3 -0
  29. package/lib/fetch/IHeadersInit.js.map +1 -0
  30. package/lib/fetch/IRequest.d.ts +18 -0
  31. package/lib/fetch/IRequest.js +3 -0
  32. package/lib/fetch/IRequest.js.map +1 -0
  33. package/lib/fetch/IRequestInit.d.ts +15 -0
  34. package/lib/fetch/IRequestInit.js +3 -0
  35. package/lib/fetch/IRequestInit.js.map +1 -0
  36. package/lib/fetch/IResponse.d.ts +20 -0
  37. package/lib/{window → fetch}/IResponse.js +0 -0
  38. package/lib/{window → fetch}/IResponse.js.map +1 -1
  39. package/lib/fetch/IResponseInit.d.ts +9 -0
  40. package/lib/fetch/IResponseInit.js +3 -0
  41. package/lib/fetch/IResponseInit.js.map +1 -0
  42. package/lib/fetch/Request.d.ts +69 -0
  43. package/lib/fetch/Request.js +179 -0
  44. package/lib/fetch/Request.js.map +1 -0
  45. package/lib/fetch/ResourceFetchHandler.d.ts +22 -0
  46. package/lib/fetch/{ResourceFetcher.js → ResourceFetchHandler.js} +19 -26
  47. package/lib/fetch/ResourceFetchHandler.js.map +1 -0
  48. package/lib/fetch/Response.d.ts +69 -0
  49. package/lib/fetch/Response.js +179 -0
  50. package/lib/fetch/Response.js.map +1 -0
  51. package/lib/file/Blob.d.ts +8 -1
  52. package/lib/file/Blob.js +48 -0
  53. package/lib/file/Blob.js.map +1 -1
  54. package/lib/file/IBlob.d.ts +10 -0
  55. package/lib/file/IBlob.js +3 -0
  56. package/lib/file/IBlob.js.map +1 -0
  57. package/lib/form-data/IFormData.d.ts +30 -0
  58. package/lib/form-data/IFormData.js +3 -0
  59. package/lib/form-data/IFormData.js.map +1 -0
  60. package/lib/index.d.ts +3 -5
  61. package/lib/index.js +3 -3
  62. package/lib/index.js.map +1 -1
  63. package/lib/location/URL.d.ts +10 -10
  64. package/lib/location/URL.js +34 -34
  65. package/lib/location/URL.js.map +1 -1
  66. package/lib/mutation-observer/MutationObserver.js.map +1 -1
  67. package/lib/nodes/character-data/CharacterData.d.ts +12 -12
  68. package/lib/nodes/character-data/CharacterData.js +24 -24
  69. package/lib/nodes/character-data/CharacterData.js.map +1 -1
  70. package/lib/nodes/document/Document.d.ts +8 -9
  71. package/lib/nodes/document/Document.js +18 -8
  72. package/lib/nodes/document/Document.js.map +1 -1
  73. package/lib/nodes/document/DocumentReadyStateManager.js.map +1 -1
  74. package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.js.map +1 -1
  75. package/lib/nodes/html-link-element/HTMLLinkElement.js +3 -3
  76. package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
  77. package/lib/nodes/html-script-element/ScriptUtility.js +3 -9
  78. package/lib/nodes/html-script-element/ScriptUtility.js.map +1 -1
  79. package/lib/query-selector/SelectorItem.d.ts +8 -1
  80. package/lib/query-selector/SelectorItem.js +8 -1
  81. package/lib/query-selector/SelectorItem.js.map +1 -1
  82. package/lib/window/IWindow.d.ts +20 -16
  83. package/lib/window/Window.d.ts +59 -12
  84. package/lib/window/Window.js +117 -70
  85. package/lib/window/Window.js.map +1 -1
  86. package/lib/xml-parser/XMLParser.js +1 -4
  87. package/lib/xml-parser/XMLParser.js.map +1 -1
  88. package/package.json +22 -21
  89. package/src/async-task-manager/AsyncTaskManager.ts +128 -0
  90. package/src/cookie/CookieUtility.ts +1 -4
  91. package/src/custom-element/CustomElementRegistry.ts +1 -1
  92. package/src/dom-token-list/DOMTokenList.ts +0 -4
  93. package/src/fetch/FetchHandler.ts +54 -0
  94. package/src/fetch/Headers.ts +7 -0
  95. package/src/fetch/IAbortSignal.ts +34 -0
  96. package/src/fetch/IBody.ts +18 -0
  97. package/src/fetch/IHeaders.ts +18 -0
  98. package/src/fetch/IHeadersInit.ts +5 -0
  99. package/src/fetch/IRequest.ts +59 -0
  100. package/src/fetch/IRequestInit.ts +41 -0
  101. package/src/fetch/IResponse.ts +22 -0
  102. package/src/fetch/IResponseInit.ts +10 -0
  103. package/src/fetch/Request.ts +149 -0
  104. package/src/fetch/ResourceFetchHandler.ts +47 -0
  105. package/src/fetch/Response.ts +149 -0
  106. package/src/file/Blob.ts +12 -1
  107. package/src/file/IBlob.ts +10 -0
  108. package/src/form-data/IFormData.ts +33 -0
  109. package/src/index.ts +2 -6
  110. package/src/location/URL.ts +39 -38
  111. package/src/mutation-observer/MutationObserver.ts +1 -1
  112. package/src/nodes/character-data/CharacterData.ts +18 -17
  113. package/src/nodes/document/Document.ts +24 -9
  114. package/src/nodes/document/DocumentReadyStateManager.ts +1 -1
  115. package/src/nodes/html-input-element/HTMLInputElementValueSanitizer.ts +1 -1
  116. package/src/nodes/html-link-element/HTMLLinkElement.ts +7 -7
  117. package/src/nodes/html-script-element/ScriptUtility.ts +3 -9
  118. package/src/query-selector/SelectorItem.ts +12 -4
  119. package/src/window/IWindow.ts +14 -16
  120. package/src/window/Window.ts +103 -70
  121. package/src/xml-parser/XMLParser.ts +1 -4
  122. package/lib/fetch/ResourceFetcher.d.ts +0 -30
  123. package/lib/fetch/ResourceFetcher.js.map +0 -1
  124. package/lib/url-search-params/URLSearchParams.d.ts +0 -87
  125. package/lib/url-search-params/URLSearchParams.js +0 -196
  126. package/lib/url-search-params/URLSearchParams.js.map +0 -1
  127. package/lib/window/AsyncTaskManager.d.ts +0 -54
  128. package/lib/window/AsyncTaskManager.js.map +0 -1
  129. package/lib/window/AsyncTaskTypeEnum.d.ts +0 -5
  130. package/lib/window/AsyncTaskTypeEnum.js +0 -9
  131. package/lib/window/AsyncTaskTypeEnum.js.map +0 -1
  132. package/lib/window/IFetchOptions.d.ts +0 -13
  133. package/lib/window/IFetchOptions.js.map +0 -1
  134. package/lib/window/IResponse.d.ts +0 -32
  135. package/src/fetch/ResourceFetcher.ts +0 -55
  136. package/src/url-search-params/URLSearchParams.ts +0 -198
  137. package/src/window/AsyncTaskManager.ts +0 -127
  138. package/src/window/AsyncTaskTypeEnum.ts +0 -5
  139. package/src/window/IFetchOptions.ts +0 -11
  140. package/src/window/IResponse.ts +0 -34
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Handles async tasks.
3
+ */
4
+ export default class AsyncTaskManager {
5
+ private static taskID = 0;
6
+ private runningTasks: number[] = [];
7
+ private runningTimers: NodeJS.Timeout[] = [];
8
+ private queue: { resolve: () => void; reject: (error: Error) => void }[] = [];
9
+
10
+ /**
11
+ * Returns a promise that is fulfilled when async tasks are complete.
12
+ * This method is not part of the HTML standard.
13
+ *
14
+ * @returns Promise.
15
+ */
16
+ public async whenComplete(): Promise<void> {
17
+ return new Promise((resolve, reject) => {
18
+ const timerID = global.setTimeout(() => {
19
+ this.endTimer(timerID);
20
+ }, 0);
21
+ this.startTimer(timerID);
22
+ this.queue.push({ resolve, reject });
23
+ });
24
+ }
25
+
26
+ /**
27
+ * Cancels all tasks.
28
+ *
29
+ * @param [error] Error.
30
+ */
31
+ public cancelAll(error?: Error): void {
32
+ for (const timerID of this.runningTimers) {
33
+ global.clearTimeout(timerID);
34
+ }
35
+
36
+ const promises = this.queue;
37
+
38
+ this.runningTasks = [];
39
+ this.runningTimers = [];
40
+ this.queue = [];
41
+
42
+ for (const promise of promises) {
43
+ if (error) {
44
+ promise.reject(error);
45
+ } else {
46
+ promise.resolve();
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Starts a timer.
53
+ *
54
+ * @param timerID Timer ID.
55
+ */
56
+ public startTimer(timerID: NodeJS.Timeout): void {
57
+ this.runningTimers.push(timerID);
58
+ }
59
+
60
+ /**
61
+ * Ends a timer.
62
+ *
63
+ * @param timerID Timer ID.
64
+ */
65
+ public endTimer(timerID: NodeJS.Timeout): void {
66
+ const index = this.runningTimers.indexOf(timerID);
67
+ if (index !== -1) {
68
+ this.runningTimers.splice(index, 1);
69
+ }
70
+ if (!this.runningTasks.length && !this.runningTimers.length) {
71
+ this.cancelAll();
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Starts an async task.
77
+ *
78
+ * @returns Task ID.
79
+ */
80
+ public startTask(): number {
81
+ const taskID = this.newTaskID();
82
+ this.runningTasks.push(taskID);
83
+ return taskID;
84
+ }
85
+
86
+ /**
87
+ * Ends an async task.
88
+ *
89
+ * @param taskID Task ID.
90
+ */
91
+ public endTask(taskID: number): void {
92
+ const index = this.runningTasks.indexOf(taskID);
93
+ if (index !== -1) {
94
+ this.runningTasks.splice(index, 1);
95
+ }
96
+ if (!this.runningTasks.length && !this.runningTimers.length) {
97
+ this.cancelAll();
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Returns the amount of running tasks.
103
+ *
104
+ * @returns Count.
105
+ */
106
+ public getTaskCount(): number {
107
+ return this.runningTasks.length;
108
+ }
109
+
110
+ /**
111
+ * Returns the amount of running timers.
112
+ *
113
+ * @returns Count.
114
+ */
115
+ public getTimerCount(): number {
116
+ return this.runningTimers.length;
117
+ }
118
+
119
+ /**
120
+ * Returns a new task ID.
121
+ *
122
+ * @returns Task ID.
123
+ */
124
+ private newTaskID(): number {
125
+ (<typeof AsyncTaskManager>this.constructor).taskID++;
126
+ return (<typeof AsyncTaskManager>this.constructor).taskID;
127
+ }
128
+ }
@@ -14,10 +14,7 @@ export default class CookieUtility {
14
14
  */
15
15
  public static getCookieString(location: Location, cookies: string, newCookie): string {
16
16
  const newCookieParts = newCookie.split(';');
17
- const [newCookieName, newCookieValue] = newCookieParts
18
- .shift()
19
- .trim()
20
- .split('=');
17
+ const [newCookieName, newCookieValue] = newCookieParts.shift().trim().split('=');
21
18
  let isExpired = false;
22
19
 
23
20
  for (const part of newCookieParts) {
@@ -84,7 +84,7 @@ export default class CustomElementRegistry {
84
84
  if (this.get(upperTagName)) {
85
85
  return Promise.resolve();
86
86
  }
87
- return new Promise(resolve => {
87
+ return new Promise((resolve) => {
88
88
  this._callbacks[upperTagName] = this._callbacks[upperTagName] || [];
89
89
  this._callbacks[upperTagName].push(resolve);
90
90
  });
@@ -84,8 +84,6 @@ export default class DOMTokenList implements IDOMTokenList {
84
84
 
85
85
  /**
86
86
  * Returns an iterator, allowing you to go through all values of the key/value pairs contained in this object.
87
- *
88
- *
89
87
  */
90
88
  public values(): IterableIterator<string> {
91
89
  const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
@@ -94,8 +92,6 @@ export default class DOMTokenList implements IDOMTokenList {
94
92
 
95
93
  /**
96
94
  * Returns an iterator, allowing you to go through all key/value pairs contained in this object.
97
- *
98
- *
99
95
  */
100
96
  public entries(): IterableIterator<[number, string]> {
101
97
  const attr = this._ownerElement.getAttributeNS(null, this._attributeName);
@@ -0,0 +1,54 @@
1
+ import RelativeURL from '../location/RelativeURL';
2
+ import IRequestInit from './IRequestInit';
3
+ import IDocument from '../nodes/document/IDocument';
4
+ import IResponse from './IResponse';
5
+
6
+ /**
7
+ * Helper class for performing fetch.
8
+ */
9
+ export default class FetchHandler {
10
+ /**
11
+ * Returns resource data asynchonously.
12
+ *
13
+ * @param document Document.
14
+ * @param url URL to resource.
15
+ * @param [init] Init.
16
+ * @returns Response.
17
+ */
18
+ public static fetch(document: IDocument, url: string, init?: IRequestInit): Promise<IResponse> {
19
+ // We want to only load NodeFetch when it is needed to improve performance and not have direct dependencies to server side packages.
20
+ const nodeFetch = require('node-fetch');
21
+ const Response = require('./Response').default;
22
+ const taskManager = document.defaultView.happyDOM.asyncTaskManager;
23
+
24
+ return new Promise((resolve, reject) => {
25
+ const taskID = taskManager.startTask();
26
+
27
+ nodeFetch(RelativeURL.getAbsoluteURL(document.defaultView.location, url), init)
28
+ .then((response) => {
29
+ if (taskManager.getTaskCount() === 0) {
30
+ reject(new Error('Failed to complete fetch request. Task was canceled.'));
31
+ } else {
32
+ response.constructor['_ownerDocument'] = document;
33
+
34
+ for (const key of Object.keys(Response.prototype)) {
35
+ if (Response.prototype.hasOwnProperty(key) && key !== 'constructor') {
36
+ if (typeof Response.prototype[key] === 'function') {
37
+ response[key] = Response.prototype[key].bind(response);
38
+ } else {
39
+ response[key] = Response.prototype[key];
40
+ }
41
+ }
42
+ }
43
+
44
+ taskManager.endTask(taskID);
45
+ resolve(response);
46
+ }
47
+ })
48
+ .catch((error) => {
49
+ reject(error);
50
+ taskManager.cancelAll(error);
51
+ });
52
+ });
53
+ }
54
+ }
@@ -0,0 +1,7 @@
1
+ import * as NodeFetch from 'node-fetch';
2
+ import IHeaders from './IHeaders';
3
+
4
+ /**
5
+ * Fetch headers.
6
+ */
7
+ export default class Headers extends NodeFetch.Headers implements IHeaders {}
@@ -0,0 +1,34 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ /**
4
+ * Abort signal.
5
+ */
6
+ export default interface IAbortSignal {
7
+ aborted: boolean;
8
+
9
+ addEventListener: (
10
+ type: 'abort',
11
+ listener: (this: IAbortSignal, event: any) => any,
12
+ options?:
13
+ | boolean
14
+ | {
15
+ capture?: boolean | undefined;
16
+ once?: boolean | undefined;
17
+ passive?: boolean | undefined;
18
+ }
19
+ ) => void;
20
+
21
+ removeEventListener: (
22
+ type: 'abort',
23
+ listener: (this: IAbortSignal, event: any) => any,
24
+ options?:
25
+ | boolean
26
+ | {
27
+ capture?: boolean | undefined;
28
+ }
29
+ ) => void;
30
+
31
+ dispatchEvent: (event: any) => boolean;
32
+
33
+ onabort: null | ((this: IAbortSignal, event: any) => void);
34
+ }
@@ -0,0 +1,18 @@
1
+ import IBlob from '../file/IBlob';
2
+
3
+ /**
4
+ * Fetch response.
5
+ */
6
+ export default interface IBody {
7
+ readonly body: NodeJS.ReadableStream;
8
+ readonly bodyUsed: boolean;
9
+ readonly size: number;
10
+ readonly timeout: number;
11
+
12
+ arrayBuffer(): Promise<ArrayBuffer>;
13
+ blob(): Promise<IBlob>;
14
+ buffer(): Promise<Buffer>;
15
+ json(): Promise<unknown>;
16
+ text(): Promise<string>;
17
+ textConverted(): Promise<string>;
18
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Fetch headers.
3
+ */
4
+ export default interface IHeaders extends Iterable<[string, string]> {
5
+ forEach(callback: (value: string, name: string) => void): void;
6
+ append(name: string, value: string): void;
7
+ delete(name: string): void;
8
+ get(name: string): string | null;
9
+ has(name: string): boolean;
10
+ raw(): { [k: string]: string[] };
11
+ set(name: string, value: string): void;
12
+
13
+ // Iterable methods
14
+ entries(): IterableIterator<[string, string]>;
15
+ keys(): IterableIterator<string>;
16
+ values(): IterableIterator<string>;
17
+ [Symbol.iterator](): Iterator<[string, string]>;
18
+ }
@@ -0,0 +1,5 @@
1
+ import IHeaders from './IHeaders';
2
+
3
+ type IHeadersInit = string[][] | { [key: string]: string } | IHeaders;
4
+
5
+ export default IHeadersInit;
@@ -0,0 +1,59 @@
1
+ import IHeaders from './IHeaders';
2
+ import IBody from './IBody';
3
+
4
+ /**
5
+ * Fetch request.
6
+ */
7
+ export default interface IRequest extends IBody {
8
+ readonly headers: IHeaders;
9
+ readonly method: string;
10
+ readonly redirect: 'error' | 'follow' | 'manual';
11
+ readonly referrer: string;
12
+ readonly url: string;
13
+
14
+ /**
15
+ * Returns a clone.
16
+ *
17
+ * @returns Clone.
18
+ */
19
+ clone(): IRequest;
20
+
21
+ // Not implemented:
22
+ // Readonly cache: 'default' | 'force-cache' | 'no-cache' | 'no-store' | 'only-if-cached' | 'reload';
23
+ // Readonly credentials: 'include' | 'omit' | 'same-origin';
24
+ // Readonly destination:
25
+ // | ''
26
+ // | 'object'
27
+ // | 'audio'
28
+ // | 'audioworklet'
29
+ // | 'document'
30
+ // | 'embed'
31
+ // | 'font'
32
+ // | 'frame'
33
+ // | 'iframe'
34
+ // | 'image'
35
+ // | 'manifest'
36
+ // | 'paintworklet'
37
+ // | 'report'
38
+ // | 'script'
39
+ // | 'sharedworker'
40
+ // | 'style'
41
+ // | 'track'
42
+ // | 'video'
43
+ // | 'worker'
44
+ // | 'xslt';
45
+ // Readonly referrerPolicy:
46
+ // | ''
47
+ // | 'same-origin'
48
+ // | 'no-referrer'
49
+ // | 'no-referrer-when-downgrade'
50
+ // | 'origin'
51
+ // | 'origin-when-cross-origin'
52
+ // | 'strict-origin'
53
+ // | 'strict-origin-when-cross-origin'
54
+ // | 'unsafe-url';
55
+ // Readonly signal: AbortSignal;
56
+ // Readonly integrity: string;
57
+ // Readonly keepalive: boolean;
58
+ // Readonly mode: 'same-origin' | 'cors' | 'navigate' | 'no-cors';
59
+ }
@@ -0,0 +1,41 @@
1
+ import IHeadersInit from './IHeadersInit';
2
+ import IAbortSignal from './IAbortSignal';
3
+ import { URLSearchParams } from 'url';
4
+ import IFormData from '../form-data/IFormData';
5
+
6
+ /**
7
+ * Fetch request init.
8
+ */
9
+ export default interface IRequestInit {
10
+ body?:
11
+ | ArrayBuffer
12
+ | ArrayBufferView
13
+ | NodeJS.ReadableStream
14
+ | string
15
+ | URLSearchParams
16
+ | IFormData
17
+ | null;
18
+ headers?: IHeadersInit;
19
+ method?: string;
20
+ redirect?: 'error' | 'manual' | 'follow';
21
+ signal?: IAbortSignal | null;
22
+
23
+ // Not implemented:
24
+ // Cache?: 'default' | 'force-cache' | 'no-cache' | 'no-store' | 'only-if-cached' | 'reload';
25
+ // Credentials?: 'include' | 'omit' | 'same-origin';
26
+ // Integrity?: string;
27
+ // Keepalive?: boolean;
28
+ // Mode?: 'same-origin' | 'cors' | 'navigate' | 'no-cors';
29
+ // Referrer?: string;
30
+ // ReferrerPolicy?:
31
+ // | ''
32
+ // | 'same-origin'
33
+ // | 'no-referrer'
34
+ // | 'no-referrer-when-downgrade'
35
+ // | 'origin'
36
+ // | 'origin-when-cross-origin'
37
+ // | 'strict-origin'
38
+ // | 'strict-origin-when-cross-origin'
39
+ // | 'unsafe-url';
40
+ // Window?: unknown;
41
+ }
@@ -0,0 +1,22 @@
1
+ import IHeaders from './IHeaders';
2
+ import IBody from './IBody';
3
+
4
+ /**
5
+ * Fetch response.
6
+ */
7
+ export default interface IResponse extends IBody {
8
+ readonly headers: IHeaders;
9
+ readonly ok: boolean;
10
+ readonly redirected: boolean;
11
+ readonly status: number;
12
+ readonly statusText: string;
13
+ readonly type: 'basic' | 'cors' | 'default' | 'error' | 'opaque' | 'opaqueredirect';
14
+ readonly url: string;
15
+
16
+ /**
17
+ * Returns a clone.
18
+ *
19
+ * @returns Clone.
20
+ */
21
+ clone(): IResponse;
22
+ }
@@ -0,0 +1,10 @@
1
+ import IHeaders from './IHeaders';
2
+
3
+ /**
4
+ * Response init.
5
+ */
6
+ export default interface IResponseInit {
7
+ headers?: IHeaders | string[][] | Record<string, string>;
8
+ status?: number;
9
+ statusText?: string;
10
+ }
@@ -0,0 +1,149 @@
1
+ import * as NodeFetch from 'node-fetch';
2
+ import IRequest from './IRequest';
3
+ import IBlob from '../file/IBlob';
4
+ import IDocument from '../nodes/document/IDocument';
5
+
6
+ /**
7
+ * Fetch request.
8
+ */
9
+ export default class Request extends NodeFetch.Request implements IRequest {
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 Request>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 Request>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 Request>this.constructor)._ownerDocument.defaultView.happyDOM
145
+ .asyncTaskManager;
146
+ reject(error);
147
+ taskManager.cancelAll(error);
148
+ }
149
+ }
@@ -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
+ }