happy-dom 2.49.1 → 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.
- package/.eslintrc.js +7 -6
- package/.prettierrc.js +2 -1
- package/lib/async-task-manager/AsyncTaskManager.d.ts +65 -0
- package/lib/{window → async-task-manager}/AsyncTaskManager.js +52 -55
- package/lib/async-task-manager/AsyncTaskManager.js.map +1 -0
- package/lib/cookie/CookieUtility.js +1 -4
- package/lib/cookie/CookieUtility.js.map +1 -1
- package/lib/custom-element/CustomElementRegistry.js.map +1 -1
- package/lib/dom-token-list/DOMTokenList.d.ts +0 -4
- package/lib/dom-token-list/DOMTokenList.js +0 -4
- package/lib/dom-token-list/DOMTokenList.js.map +1 -1
- package/lib/fetch/FetchHandler.d.ts +17 -0
- package/lib/fetch/FetchHandler.js +59 -0
- package/lib/fetch/FetchHandler.js.map +1 -0
- package/lib/fetch/Headers.d.ts +7 -0
- package/lib/fetch/Headers.js +53 -0
- package/lib/fetch/Headers.js.map +1 -0
- package/lib/fetch/IAbortSignal.d.ts +16 -0
- package/lib/fetch/IAbortSignal.js +4 -0
- package/lib/fetch/IAbortSignal.js.map +1 -0
- package/lib/fetch/IBody.d.ts +17 -0
- package/lib/{window/IFetchOptions.js → fetch/IBody.js} +1 -1
- package/lib/fetch/IBody.js.map +1 -0
- package/lib/fetch/IHeaders.d.ts +18 -0
- package/lib/fetch/IHeaders.js +3 -0
- package/lib/fetch/IHeaders.js.map +1 -0
- package/lib/fetch/IHeadersInit.d.ts +5 -0
- package/lib/fetch/IHeadersInit.js +3 -0
- package/lib/fetch/IHeadersInit.js.map +1 -0
- package/lib/fetch/IRequest.d.ts +18 -0
- package/lib/fetch/IRequest.js +3 -0
- package/lib/fetch/IRequest.js.map +1 -0
- package/lib/fetch/IRequestInit.d.ts +15 -0
- package/lib/fetch/IRequestInit.js +3 -0
- package/lib/fetch/IRequestInit.js.map +1 -0
- package/lib/fetch/IResponse.d.ts +20 -0
- package/lib/{window → fetch}/IResponse.js +0 -0
- package/lib/{window → fetch}/IResponse.js.map +1 -1
- package/lib/fetch/IResponseInit.d.ts +9 -0
- package/lib/fetch/IResponseInit.js +3 -0
- package/lib/fetch/IResponseInit.js.map +1 -0
- package/lib/fetch/Request.d.ts +69 -0
- package/lib/fetch/Request.js +179 -0
- package/lib/fetch/Request.js.map +1 -0
- package/lib/fetch/ResourceFetchHandler.d.ts +22 -0
- package/lib/fetch/{ResourceFetcher.js → ResourceFetchHandler.js} +19 -26
- package/lib/fetch/ResourceFetchHandler.js.map +1 -0
- package/lib/fetch/Response.d.ts +69 -0
- package/lib/fetch/Response.js +179 -0
- package/lib/fetch/Response.js.map +1 -0
- package/lib/file/Blob.d.ts +8 -1
- package/lib/file/Blob.js +48 -0
- package/lib/file/Blob.js.map +1 -1
- package/lib/file/IBlob.d.ts +10 -0
- package/lib/file/IBlob.js +3 -0
- package/lib/file/IBlob.js.map +1 -0
- package/lib/form-data/IFormData.d.ts +30 -0
- package/lib/form-data/IFormData.js +3 -0
- package/lib/form-data/IFormData.js.map +1 -0
- package/lib/index.d.ts +3 -5
- package/lib/index.js +3 -3
- package/lib/index.js.map +1 -1
- package/lib/location/URL.d.ts +10 -10
- package/lib/location/URL.js +34 -34
- package/lib/location/URL.js.map +1 -1
- package/lib/mutation-observer/MutationObserver.js.map +1 -1
- package/lib/nodes/character-data/CharacterData.d.ts +12 -12
- package/lib/nodes/character-data/CharacterData.js +24 -24
- package/lib/nodes/character-data/CharacterData.js.map +1 -1
- package/lib/nodes/document/Document.d.ts +8 -9
- package/lib/nodes/document/Document.js +9 -8
- package/lib/nodes/document/Document.js.map +1 -1
- package/lib/nodes/document/DocumentReadyStateManager.js.map +1 -1
- package/lib/nodes/html-input-element/HTMLInputElementValueSanitizer.js.map +1 -1
- package/lib/nodes/html-link-element/HTMLLinkElement.js +3 -3
- package/lib/nodes/html-link-element/HTMLLinkElement.js.map +1 -1
- package/lib/nodes/html-script-element/ScriptUtility.js +3 -9
- package/lib/nodes/html-script-element/ScriptUtility.js.map +1 -1
- package/lib/query-selector/SelectorItem.d.ts +8 -1
- package/lib/query-selector/SelectorItem.js +8 -1
- package/lib/query-selector/SelectorItem.js.map +1 -1
- package/lib/window/IWindow.d.ts +20 -16
- package/lib/window/Window.d.ts +59 -12
- package/lib/window/Window.js +117 -70
- package/lib/window/Window.js.map +1 -1
- package/lib/xml-parser/XMLParser.js +1 -4
- package/lib/xml-parser/XMLParser.js.map +1 -1
- package/package.json +22 -21
- package/src/async-task-manager/AsyncTaskManager.ts +128 -0
- package/src/cookie/CookieUtility.ts +1 -4
- package/src/custom-element/CustomElementRegistry.ts +1 -1
- package/src/dom-token-list/DOMTokenList.ts +0 -4
- package/src/fetch/FetchHandler.ts +54 -0
- package/src/fetch/Headers.ts +7 -0
- package/src/fetch/IAbortSignal.ts +34 -0
- package/src/fetch/IBody.ts +18 -0
- package/src/fetch/IHeaders.ts +18 -0
- package/src/fetch/IHeadersInit.ts +5 -0
- package/src/fetch/IRequest.ts +59 -0
- package/src/fetch/IRequestInit.ts +41 -0
- package/src/fetch/IResponse.ts +22 -0
- package/src/fetch/IResponseInit.ts +10 -0
- package/src/fetch/Request.ts +149 -0
- package/src/fetch/ResourceFetchHandler.ts +47 -0
- package/src/fetch/Response.ts +149 -0
- package/src/file/Blob.ts +12 -1
- package/src/file/IBlob.ts +10 -0
- package/src/form-data/IFormData.ts +33 -0
- package/src/index.ts +2 -6
- package/src/location/URL.ts +39 -38
- package/src/mutation-observer/MutationObserver.ts +1 -1
- package/src/nodes/character-data/CharacterData.ts +18 -17
- package/src/nodes/document/Document.ts +12 -9
- package/src/nodes/document/DocumentReadyStateManager.ts +1 -1
- package/src/nodes/html-input-element/HTMLInputElementValueSanitizer.ts +1 -1
- package/src/nodes/html-link-element/HTMLLinkElement.ts +7 -7
- package/src/nodes/html-script-element/ScriptUtility.ts +3 -9
- package/src/query-selector/SelectorItem.ts +12 -4
- package/src/window/IWindow.ts +14 -16
- package/src/window/Window.ts +103 -70
- package/src/xml-parser/XMLParser.ts +1 -4
- package/lib/fetch/ResourceFetcher.d.ts +0 -30
- package/lib/fetch/ResourceFetcher.js.map +0 -1
- package/lib/url-search-params/URLSearchParams.d.ts +0 -87
- package/lib/url-search-params/URLSearchParams.js +0 -196
- package/lib/url-search-params/URLSearchParams.js.map +0 -1
- package/lib/window/AsyncTaskManager.d.ts +0 -54
- package/lib/window/AsyncTaskManager.js.map +0 -1
- package/lib/window/AsyncTaskTypeEnum.d.ts +0 -5
- package/lib/window/AsyncTaskTypeEnum.js +0 -9
- package/lib/window/AsyncTaskTypeEnum.js.map +0 -1
- package/lib/window/IFetchOptions.d.ts +0 -13
- package/lib/window/IFetchOptions.js.map +0 -1
- package/lib/window/IResponse.d.ts +0 -32
- package/src/fetch/ResourceFetcher.ts +0 -55
- package/src/url-search-params/URLSearchParams.ts +0 -198
- package/src/window/AsyncTaskManager.ts +0 -127
- package/src/window/AsyncTaskTypeEnum.ts +0 -5
- package/src/window/IFetchOptions.ts +0 -11
- 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,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,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,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
|
+
}
|