@cedx/base 0.7.0 → 0.8.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.
- package/ReadMe.md +1 -1
- package/lib/FileExtensions.js +1 -1
- package/lib/Hosting/Environment.d.ts +22 -0
- package/lib/Hosting/Environment.d.ts.map +1 -0
- package/lib/Hosting/Environment.js +17 -0
- package/lib/Hosting/HostEnvironment.d.ts +61 -0
- package/lib/Hosting/HostEnvironment.d.ts.map +1 -0
- package/lib/Hosting/HostEnvironment.js +56 -0
- package/lib/UI/FormExtensions.d.ts +33 -0
- package/lib/UI/FormExtensions.d.ts.map +1 -0
- package/lib/UI/FormExtensions.js +50 -0
- package/package.json +2 -5
- package/src/Client/FileExtensions.ts +1 -1
- package/src/Client/Hosting/Environment.ts +25 -0
- package/src/Client/Hosting/HostEnvironment.ts +86 -0
- package/src/Client/Hosting/tsconfig.json +13 -0
- package/src/Client/UI/FormExtensions.ts +55 -0
- package/src/Client/UI/tsconfig.json +1 -4
- package/src/Client/tsconfig.json +1 -0
- package/lib/Net/Http/HttpClient.d.ts +0 -83
- package/lib/Net/Http/HttpClient.d.ts.map +0 -1
- package/lib/Net/Http/HttpClient.js +0 -104
- package/lib/Net/Http/HttpRequestError.d.ts +0 -33
- package/lib/Net/Http/HttpRequestError.d.ts.map +0 -1
- package/lib/Net/Http/HttpRequestError.js +0 -66
- package/src/Client/Net/Http/HttpClient.ts +0 -145
- package/src/Client/Net/Http/HttpRequestError.ts +0 -75
package/ReadMe.md
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# Belin.io Base
|
|
2
|
-
   
|
|
3
3
|
|
|
4
4
|
Base library by [Cédric Belin](https://belin.io), full stack developer,
|
|
5
5
|
implemented in [C#](https://learn.microsoft.com/en-us/dotnet/csharp) and [TypeScript](https://www.typescriptlang.org).
|
package/lib/FileExtensions.js
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commonly used environment names.
|
|
3
|
+
*/
|
|
4
|
+
export declare const Environment: Readonly<{
|
|
5
|
+
/**
|
|
6
|
+
* Specifies the development environment.
|
|
7
|
+
*/
|
|
8
|
+
Development: "Development";
|
|
9
|
+
/**
|
|
10
|
+
* Specifies the production environment.
|
|
11
|
+
*/
|
|
12
|
+
Production: "Production";
|
|
13
|
+
/**
|
|
14
|
+
* Specifies the staging environment.
|
|
15
|
+
*/
|
|
16
|
+
Staging: "Staging";
|
|
17
|
+
}>;
|
|
18
|
+
/**
|
|
19
|
+
* Commonly used environment names.
|
|
20
|
+
*/
|
|
21
|
+
export type Environment = typeof Environment[keyof typeof Environment];
|
|
22
|
+
//# sourceMappingURL=Environment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Environment.d.ts","sourceRoot":"","sources":["../../src/Client/Hosting/Environment.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,eAAO,MAAM,WAAW;IAEvB;;OAEG;;IAGH;;OAEG;;IAGH;;OAEG;;EAEF,CAAC;AAEH;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,MAAM,OAAO,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commonly used environment names.
|
|
3
|
+
*/
|
|
4
|
+
export const Environment = Object.freeze({
|
|
5
|
+
/**
|
|
6
|
+
* Specifies the development environment.
|
|
7
|
+
*/
|
|
8
|
+
Development: "Development",
|
|
9
|
+
/**
|
|
10
|
+
* Specifies the production environment.
|
|
11
|
+
*/
|
|
12
|
+
Production: "Production",
|
|
13
|
+
/**
|
|
14
|
+
* Specifies the staging environment.
|
|
15
|
+
*/
|
|
16
|
+
Staging: "Staging"
|
|
17
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provides information about the hosting environment an application is running in.
|
|
3
|
+
*/
|
|
4
|
+
export declare class HostEnvironment {
|
|
5
|
+
/**
|
|
6
|
+
* The name of the application.
|
|
7
|
+
*/
|
|
8
|
+
readonly applicationName: string;
|
|
9
|
+
/**
|
|
10
|
+
* The path to the directory that contains the application content files.
|
|
11
|
+
*/
|
|
12
|
+
readonly contentRootPath: string;
|
|
13
|
+
/**
|
|
14
|
+
* The name of the environment.
|
|
15
|
+
*/
|
|
16
|
+
readonly environmentName: string;
|
|
17
|
+
/**
|
|
18
|
+
* Creates a new host environment.
|
|
19
|
+
* @param options An object providing values to initialize this instance.
|
|
20
|
+
*/
|
|
21
|
+
constructor(options?: HostEnvironmentOptions);
|
|
22
|
+
/**
|
|
23
|
+
* Checks if the current environment name is {@link Environment.Development}.
|
|
24
|
+
* @returns `true` if the environment name is {@link Environment.Development}, otherwise `false`.
|
|
25
|
+
*/
|
|
26
|
+
get isDevelopment(): boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Checks if the current environment name is {@link Environment.Production}.
|
|
29
|
+
* @returns `true` if the environment name is {@link Environment.Production}, otherwise `false`.
|
|
30
|
+
*/
|
|
31
|
+
get isProduction(): boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Checks if the current environment name is {@link Environment.Staging}.
|
|
34
|
+
* @returns `true` if the environment name is {@link Environment.Staging}, otherwise `false`.
|
|
35
|
+
*/
|
|
36
|
+
get isStaging(): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Compares the current host environment name against the specified value.
|
|
39
|
+
* @param environmentName The environment name to validate against.
|
|
40
|
+
* @returns `true` if the specified name is the same as the current environment, otherwise `false`.
|
|
41
|
+
*/
|
|
42
|
+
isEnvironment(environmentName: string): boolean;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Defines the options of a {@link HostEnvironment} instance.
|
|
46
|
+
*/
|
|
47
|
+
export type HostEnvironmentOptions = Partial<{
|
|
48
|
+
/**
|
|
49
|
+
* The name of the application.
|
|
50
|
+
*/
|
|
51
|
+
applicationName: string;
|
|
52
|
+
/**
|
|
53
|
+
* The path to the directory that contains the application content files.
|
|
54
|
+
*/
|
|
55
|
+
contentRootPath: string;
|
|
56
|
+
/**
|
|
57
|
+
* The name of the environment.
|
|
58
|
+
*/
|
|
59
|
+
environmentName: string;
|
|
60
|
+
}>;
|
|
61
|
+
//# sourceMappingURL=HostEnvironment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"HostEnvironment.d.ts","sourceRoot":"","sources":["../../src/Client/Hosting/HostEnvironment.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,qBAAa,eAAe;IAE3B;;OAEG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC;;OAEG;IACH,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IAEjC;;;OAGG;gBACS,OAAO,GAAE,sBAA2B;IAMhD;;;OAGG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;;OAGG;IACH,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;OAGG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;;OAIG;IACH,aAAa,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO;CAG/C;AAED;;GAEG;AACH,MAAM,MAAM,sBAAsB,GAAG,OAAO,CAAC;IAE5C;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;CACxB,CAAC,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Environment } from "./Environment.js";
|
|
2
|
+
/**
|
|
3
|
+
* Provides information about the hosting environment an application is running in.
|
|
4
|
+
*/
|
|
5
|
+
export class HostEnvironment {
|
|
6
|
+
/**
|
|
7
|
+
* The name of the application.
|
|
8
|
+
*/
|
|
9
|
+
applicationName;
|
|
10
|
+
/**
|
|
11
|
+
* The path to the directory that contains the application content files.
|
|
12
|
+
*/
|
|
13
|
+
contentRootPath;
|
|
14
|
+
/**
|
|
15
|
+
* The name of the environment.
|
|
16
|
+
*/
|
|
17
|
+
environmentName;
|
|
18
|
+
/**
|
|
19
|
+
* Creates a new host environment.
|
|
20
|
+
* @param options An object providing values to initialize this instance.
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.applicationName = options.applicationName ?? document.head.querySelector('meta[name="application-name"]')?.content ?? location.hostname;
|
|
24
|
+
this.contentRootPath = options.contentRootPath ?? document.head.querySelector("base")?.getAttribute("href") ?? "/";
|
|
25
|
+
this.environmentName = options.environmentName ?? Environment.Production;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Checks if the current environment name is {@link Environment.Development}.
|
|
29
|
+
* @returns `true` if the environment name is {@link Environment.Development}, otherwise `false`.
|
|
30
|
+
*/
|
|
31
|
+
get isDevelopment() {
|
|
32
|
+
return this.isEnvironment(Environment.Development);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Checks if the current environment name is {@link Environment.Production}.
|
|
36
|
+
* @returns `true` if the environment name is {@link Environment.Production}, otherwise `false`.
|
|
37
|
+
*/
|
|
38
|
+
get isProduction() {
|
|
39
|
+
return this.isEnvironment(Environment.Production);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Checks if the current environment name is {@link Environment.Staging}.
|
|
43
|
+
* @returns `true` if the environment name is {@link Environment.Staging}, otherwise `false`.
|
|
44
|
+
*/
|
|
45
|
+
get isStaging() {
|
|
46
|
+
return this.isEnvironment(Environment.Staging);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Compares the current host environment name against the specified value.
|
|
50
|
+
* @param environmentName The environment name to validate against.
|
|
51
|
+
* @returns `true` if the specified name is the same as the current environment, otherwise `false`.
|
|
52
|
+
*/
|
|
53
|
+
isEnvironment(environmentName) {
|
|
54
|
+
return this.environmentName == environmentName;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a form control.
|
|
3
|
+
*/
|
|
4
|
+
export type FormControl = HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement;
|
|
5
|
+
/**
|
|
6
|
+
* Gets all controls belonging to the specified form.
|
|
7
|
+
* @param form The form element.
|
|
8
|
+
* @returns The controls belonging to the specified form.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getFormControls(form: HTMLFormElement): FormControl[];
|
|
11
|
+
/**
|
|
12
|
+
* Returns the first invalid control from the specified form.
|
|
13
|
+
* @param form The form element.
|
|
14
|
+
* @returns The first invalid control, or `null` if all controls are valid.
|
|
15
|
+
*/
|
|
16
|
+
export declare function invalidControl(form: HTMLFormElement): FormControl | null;
|
|
17
|
+
/**
|
|
18
|
+
* Returns a value indicating whether the specified element is a form control.
|
|
19
|
+
* @param element The element to check.
|
|
20
|
+
* @returns `true` if the specified element is a form control, otherwise `false`.
|
|
21
|
+
*/
|
|
22
|
+
export declare function isFormControl(element: Element): element is FormControl;
|
|
23
|
+
/**
|
|
24
|
+
* Resets the validity of the specified element.
|
|
25
|
+
* @param element The element to process.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resetValidity(element: Element): void;
|
|
28
|
+
/**
|
|
29
|
+
* Removes whitespace from both ends of the value of the specified element.
|
|
30
|
+
* @param element The element to process.
|
|
31
|
+
*/
|
|
32
|
+
export declare function trimControl(element: Element): void;
|
|
33
|
+
//# sourceMappingURL=FormExtensions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FormExtensions.d.ts","sourceRoot":"","sources":["../../src/Client/UI/FormExtensions.ts"],"names":[],"mappings":"AAKA;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG,gBAAgB,GAAC,iBAAiB,GAAC,mBAAmB,CAAC;AAEjF;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,EAAE,CAEpE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,GAAC,IAAI,CAEtE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,IAAI,WAAW,CAEtE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAGpD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAIlD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The types of form controls that are not text inputs.
|
|
3
|
+
*/
|
|
4
|
+
const nonTextualTypes = new Set(["button", "checkbox", "file", "hidden", "image", "password", "radio", "range", "reset", "submit"]);
|
|
5
|
+
/**
|
|
6
|
+
* Gets all controls belonging to the specified form.
|
|
7
|
+
* @param form The form element.
|
|
8
|
+
* @returns The controls belonging to the specified form.
|
|
9
|
+
*/
|
|
10
|
+
export function getFormControls(form) {
|
|
11
|
+
return Array.from(form.elements).filter(isFormControl);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns the first invalid control from the specified form.
|
|
15
|
+
* @param form The form element.
|
|
16
|
+
* @returns The first invalid control, or `null` if all controls are valid.
|
|
17
|
+
*/
|
|
18
|
+
export function invalidControl(form) {
|
|
19
|
+
return form.querySelector(":not(fieldset):invalid");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Returns a value indicating whether the specified element is a form control.
|
|
23
|
+
* @param element The element to check.
|
|
24
|
+
* @returns `true` if the specified element is a form control, otherwise `false`.
|
|
25
|
+
*/
|
|
26
|
+
export function isFormControl(element) {
|
|
27
|
+
return element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resets the validity of the specified element.
|
|
31
|
+
* @param element The element to process.
|
|
32
|
+
*/
|
|
33
|
+
export function resetValidity(element) {
|
|
34
|
+
if (element instanceof HTMLFormElement)
|
|
35
|
+
getFormControls(element).forEach(control => control.setCustomValidity(""));
|
|
36
|
+
else if (isFormControl(element))
|
|
37
|
+
element.setCustomValidity("");
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Removes whitespace from both ends of the value of the specified element.
|
|
41
|
+
* @param element The element to process.
|
|
42
|
+
*/
|
|
43
|
+
export function trimControl(element) {
|
|
44
|
+
if (element instanceof HTMLFormElement)
|
|
45
|
+
getFormControls(element).forEach(trimControl);
|
|
46
|
+
else if (element instanceof HTMLInputElement && !nonTextualTypes.has(element.type))
|
|
47
|
+
element.value = element.value.trim();
|
|
48
|
+
else if (element instanceof HTMLTextAreaElement)
|
|
49
|
+
element.value = element.value.trim();
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"name": "@cedx/base",
|
|
8
8
|
"repository": "cedx/base",
|
|
9
9
|
"type": "module",
|
|
10
|
-
"version": "0.
|
|
10
|
+
"version": "0.8.0",
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@playwright/browser-chromium": "^1.54.2",
|
|
13
13
|
"@types/bootstrap": "^5.2.10",
|
|
14
14
|
"@types/chai": "^5.2.2",
|
|
15
15
|
"@types/mocha": "^10.0.10",
|
|
16
|
-
"@types/node": "^24.
|
|
16
|
+
"@types/node": "^24.3.0",
|
|
17
17
|
"@types/serve-handler": "^6.1.4",
|
|
18
18
|
"chai": "^5.2.1",
|
|
19
19
|
"esbuild": "^0.25.9",
|
|
@@ -38,9 +38,6 @@
|
|
|
38
38
|
"lib/",
|
|
39
39
|
"src/Client/"
|
|
40
40
|
],
|
|
41
|
-
"imports": {
|
|
42
|
-
"#Base/*.js": "./lib/*.js"
|
|
43
|
-
},
|
|
44
41
|
"keywords": [
|
|
45
42
|
"belin",
|
|
46
43
|
"core",
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Commonly used environment names.
|
|
3
|
+
*/
|
|
4
|
+
export const Environment = Object.freeze({
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Specifies the development environment.
|
|
8
|
+
*/
|
|
9
|
+
Development: "Development",
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Specifies the production environment.
|
|
13
|
+
*/
|
|
14
|
+
Production: "Production",
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Specifies the staging environment.
|
|
18
|
+
*/
|
|
19
|
+
Staging: "Staging"
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Commonly used environment names.
|
|
24
|
+
*/
|
|
25
|
+
export type Environment = typeof Environment[keyof typeof Environment];
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import {Environment} from "./Environment.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Provides information about the hosting environment an application is running in.
|
|
5
|
+
*/
|
|
6
|
+
export class HostEnvironment {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The name of the application.
|
|
10
|
+
*/
|
|
11
|
+
readonly applicationName: string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The path to the directory that contains the application content files.
|
|
15
|
+
*/
|
|
16
|
+
readonly contentRootPath: string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The name of the environment.
|
|
20
|
+
*/
|
|
21
|
+
readonly environmentName: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Creates a new host environment.
|
|
25
|
+
* @param options An object providing values to initialize this instance.
|
|
26
|
+
*/
|
|
27
|
+
constructor(options: HostEnvironmentOptions = {}) {
|
|
28
|
+
this.applicationName = options.applicationName ?? document.head.querySelector<HTMLMetaElement>('meta[name="application-name"]')?.content ?? location.hostname;
|
|
29
|
+
this.contentRootPath = options.contentRootPath ?? document.head.querySelector("base")?.getAttribute("href") ?? "/";
|
|
30
|
+
this.environmentName = options.environmentName ?? Environment.Production;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Checks if the current environment name is {@link Environment.Development}.
|
|
35
|
+
* @returns `true` if the environment name is {@link Environment.Development}, otherwise `false`.
|
|
36
|
+
*/
|
|
37
|
+
get isDevelopment(): boolean {
|
|
38
|
+
return this.isEnvironment(Environment.Development);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Checks if the current environment name is {@link Environment.Production}.
|
|
43
|
+
* @returns `true` if the environment name is {@link Environment.Production}, otherwise `false`.
|
|
44
|
+
*/
|
|
45
|
+
get isProduction(): boolean {
|
|
46
|
+
return this.isEnvironment(Environment.Production);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Checks if the current environment name is {@link Environment.Staging}.
|
|
51
|
+
* @returns `true` if the environment name is {@link Environment.Staging}, otherwise `false`.
|
|
52
|
+
*/
|
|
53
|
+
get isStaging(): boolean {
|
|
54
|
+
return this.isEnvironment(Environment.Staging);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Compares the current host environment name against the specified value.
|
|
59
|
+
* @param environmentName The environment name to validate against.
|
|
60
|
+
* @returns `true` if the specified name is the same as the current environment, otherwise `false`.
|
|
61
|
+
*/
|
|
62
|
+
isEnvironment(environmentName: string): boolean {
|
|
63
|
+
return this.environmentName == environmentName;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Defines the options of a {@link HostEnvironment} instance.
|
|
69
|
+
*/
|
|
70
|
+
export type HostEnvironmentOptions = Partial<{
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The name of the application.
|
|
74
|
+
*/
|
|
75
|
+
applicationName: string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The path to the directory that contains the application content files.
|
|
79
|
+
*/
|
|
80
|
+
contentRootPath: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The name of the environment.
|
|
84
|
+
*/
|
|
85
|
+
environmentName: string;
|
|
86
|
+
}>;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../../tsconfig.json",
|
|
3
|
+
"include": ["**/*.ts"],
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"composite": true,
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"noEmit": false,
|
|
9
|
+
"outDir": "../../../lib/Hosting",
|
|
10
|
+
"rootDir": ".",
|
|
11
|
+
"tsBuildInfoFile": "../../../var/Hosting.tsbuildinfo"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The types of form controls that are not text inputs.
|
|
3
|
+
*/
|
|
4
|
+
const nonTextualTypes = new Set<string>(["button", "checkbox", "file", "hidden", "image", "password", "radio", "range", "reset", "submit"]);
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Represents a form control.
|
|
8
|
+
*/
|
|
9
|
+
export type FormControl = HTMLInputElement|HTMLSelectElement|HTMLTextAreaElement;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Gets all controls belonging to the specified form.
|
|
13
|
+
* @param form The form element.
|
|
14
|
+
* @returns The controls belonging to the specified form.
|
|
15
|
+
*/
|
|
16
|
+
export function getFormControls(form: HTMLFormElement): FormControl[] {
|
|
17
|
+
return Array.from(form.elements).filter(isFormControl);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns the first invalid control from the specified form.
|
|
22
|
+
* @param form The form element.
|
|
23
|
+
* @returns The first invalid control, or `null` if all controls are valid.
|
|
24
|
+
*/
|
|
25
|
+
export function invalidControl(form: HTMLFormElement): FormControl|null {
|
|
26
|
+
return form.querySelector(":not(fieldset):invalid");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns a value indicating whether the specified element is a form control.
|
|
31
|
+
* @param element The element to check.
|
|
32
|
+
* @returns `true` if the specified element is a form control, otherwise `false`.
|
|
33
|
+
*/
|
|
34
|
+
export function isFormControl(element: Element): element is FormControl {
|
|
35
|
+
return element instanceof HTMLInputElement || element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Resets the validity of the specified element.
|
|
40
|
+
* @param element The element to process.
|
|
41
|
+
*/
|
|
42
|
+
export function resetValidity(element: Element): void {
|
|
43
|
+
if (element instanceof HTMLFormElement) getFormControls(element).forEach(control => control.setCustomValidity(""));
|
|
44
|
+
else if (isFormControl(element)) element.setCustomValidity("");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Removes whitespace from both ends of the value of the specified element.
|
|
49
|
+
* @param element The element to process.
|
|
50
|
+
*/
|
|
51
|
+
export function trimControl(element: Element): void {
|
|
52
|
+
if (element instanceof HTMLFormElement) getFormControls(element).forEach(trimControl);
|
|
53
|
+
else if (element instanceof HTMLInputElement && !nonTextualTypes.has(element.type)) element.value = element.value.trim();
|
|
54
|
+
else if (element instanceof HTMLTextAreaElement) element.value = element.value.trim();
|
|
55
|
+
}
|
package/src/Client/tsconfig.json
CHANGED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Performs HTTP requests.
|
|
3
|
-
*/
|
|
4
|
-
export declare class HttpClient {
|
|
5
|
-
#private;
|
|
6
|
-
/**
|
|
7
|
-
* The base URL of the remote service.
|
|
8
|
-
*/
|
|
9
|
-
readonly baseAddress: URL;
|
|
10
|
-
/**
|
|
11
|
-
* Creates a new HTTP client.
|
|
12
|
-
* @param options An object providing values to initialize this instance.
|
|
13
|
-
*/
|
|
14
|
-
constructor(options?: HttpClientOptions);
|
|
15
|
-
/**
|
|
16
|
-
* Performs a DELETE request.
|
|
17
|
-
* @param url The URL of the resource to fetch.
|
|
18
|
-
* @param options The request options.
|
|
19
|
-
* @returns The server response.
|
|
20
|
-
*/
|
|
21
|
-
delete(url?: string | URL, options?: RequestInit): Promise<Response>;
|
|
22
|
-
/**
|
|
23
|
-
* Performs a GET request.
|
|
24
|
-
* @param url The URL of the resource to fetch.
|
|
25
|
-
* @param options The request options.
|
|
26
|
-
* @returns The server response.
|
|
27
|
-
*/
|
|
28
|
-
get(url?: string | URL, options?: RequestInit): Promise<Response>;
|
|
29
|
-
/**
|
|
30
|
-
* Performs a PATCH request.
|
|
31
|
-
* @param url The URL of the resource to fetch.
|
|
32
|
-
* @param body The request body.
|
|
33
|
-
* @param options The request options.
|
|
34
|
-
* @returns The server response.
|
|
35
|
-
*/
|
|
36
|
-
patch(url?: string | URL, body?: unknown, options?: RequestInit): Promise<Response>;
|
|
37
|
-
/**
|
|
38
|
-
* Performs a POST request.
|
|
39
|
-
* @param url The URL of the resource to fetch.
|
|
40
|
-
* @param body The request body.
|
|
41
|
-
* @param options The request options.
|
|
42
|
-
* @returns The server response.
|
|
43
|
-
*/
|
|
44
|
-
post(url?: string | URL, body?: unknown, options?: RequestInit): Promise<Response>;
|
|
45
|
-
/**
|
|
46
|
-
* Performs a PUT request.
|
|
47
|
-
* @param url The URL of the resource to fetch.
|
|
48
|
-
* @param body The request body.
|
|
49
|
-
* @param options The request options.
|
|
50
|
-
* @returns The server response.
|
|
51
|
-
*/
|
|
52
|
-
put(url?: string | URL, body?: unknown, options?: RequestInit): Promise<Response>;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Defines the options of a {@link HttpClient} instance.
|
|
56
|
-
*/
|
|
57
|
-
export type HttpClientOptions = Partial<{
|
|
58
|
-
/**
|
|
59
|
-
* The base URL of the remote service.
|
|
60
|
-
*/
|
|
61
|
-
baseUrl: string | URL;
|
|
62
|
-
/**
|
|
63
|
-
* The function returning the component used as loading indicator.
|
|
64
|
-
*/
|
|
65
|
-
loadingIndicator: () => ILoadingIndicator | null;
|
|
66
|
-
}>;
|
|
67
|
-
/**
|
|
68
|
-
* A component that shows up when an HTTP request starts, and hides when all concurrent HTTP requests are completed.
|
|
69
|
-
*/
|
|
70
|
-
export interface ILoadingIndicator {
|
|
71
|
-
/**
|
|
72
|
-
* Starts the loading indicator.
|
|
73
|
-
*/
|
|
74
|
-
start: () => void;
|
|
75
|
-
/**
|
|
76
|
-
* Stops the loading indicator.
|
|
77
|
-
* @param options Value indicating whether to force the loading indicator to stop.
|
|
78
|
-
*/
|
|
79
|
-
stop: (options?: {
|
|
80
|
-
force?: boolean;
|
|
81
|
-
}) => void;
|
|
82
|
-
}
|
|
83
|
-
//# sourceMappingURL=HttpClient.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"HttpClient.d.ts","sourceRoot":"","sources":["../../../src/Client/Net/Http/HttpClient.ts"],"names":[],"mappings":"AAIA;;GAEG;AACH,qBAAa,UAAU;;IAEtB;;OAEG;IACH,QAAQ,CAAC,WAAW,EAAE,GAAG,CAAC;IAO1B;;;OAGG;gBACS,OAAO,GAAE,iBAAsB;IAM3C;;;;;OAKG;IACH,MAAM,CAAC,GAAG,CAAC,EAAE,MAAM,GAAC,GAAG,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIlE;;;;;OAKG;IACH,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,GAAC,GAAG,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAI/D;;;;;;OAMG;IACH,KAAK,CAAC,GAAG,CAAC,EAAE,MAAM,GAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIjF;;;;;;OAMG;IACH,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,GAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;IAIhF;;;;;;OAMG;IACH,GAAG,CAAC,GAAG,CAAC,EAAE,MAAM,GAAC,GAAG,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC;CAiC/E;AAED;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC;IAEvC;;OAEG;IACH,OAAO,EAAE,MAAM,GAAC,GAAG,CAAC;IAEpB;;OAEG;IACH,gBAAgB,EAAE,MAAM,iBAAiB,GAAC,IAAI,CAAC;CAC/C,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAEjC;;OAEG;IACH,KAAK,EAAE,MAAM,IAAI,CAAC;IAElB;;;OAGG;IACH,IAAI,EAAE,CAAC,OAAO,CAAC,EAAE;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAC,KAAK,IAAI,CAAC;CAC5C"}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import { MediaType } from "../Mime/MediaType.js";
|
|
2
|
-
import { HttpMethod } from "./HttpMethod.js";
|
|
3
|
-
import { HttpRequestError } from "./HttpRequestError.js";
|
|
4
|
-
/**
|
|
5
|
-
* Performs HTTP requests.
|
|
6
|
-
*/
|
|
7
|
-
export class HttpClient {
|
|
8
|
-
/**
|
|
9
|
-
* The base URL of the remote service.
|
|
10
|
-
*/
|
|
11
|
-
baseAddress;
|
|
12
|
-
/**
|
|
13
|
-
* The function returning the component used as loading indicator.
|
|
14
|
-
*/
|
|
15
|
-
#loadingIndicator;
|
|
16
|
-
/**
|
|
17
|
-
* Creates a new HTTP client.
|
|
18
|
-
* @param options An object providing values to initialize this instance.
|
|
19
|
-
*/
|
|
20
|
-
constructor(options = {}) {
|
|
21
|
-
const url = options.baseUrl ? (options.baseUrl instanceof URL ? options.baseUrl.href : options.baseUrl) : document.baseURI;
|
|
22
|
-
this.baseAddress = new URL(url.endsWith("/") ? url : `${url}/`);
|
|
23
|
-
this.#loadingIndicator = options.loadingIndicator ?? (() => document.body.querySelector("loading-indicator, .loading-indicator"));
|
|
24
|
-
}
|
|
25
|
-
/**
|
|
26
|
-
* Performs a DELETE request.
|
|
27
|
-
* @param url The URL of the resource to fetch.
|
|
28
|
-
* @param options The request options.
|
|
29
|
-
* @returns The server response.
|
|
30
|
-
*/
|
|
31
|
-
delete(url, options) {
|
|
32
|
-
return this.#fetch(HttpMethod.Delete, url, null, options);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Performs a GET request.
|
|
36
|
-
* @param url The URL of the resource to fetch.
|
|
37
|
-
* @param options The request options.
|
|
38
|
-
* @returns The server response.
|
|
39
|
-
*/
|
|
40
|
-
get(url, options) {
|
|
41
|
-
return this.#fetch(HttpMethod.Get, url, null, options);
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Performs a PATCH request.
|
|
45
|
-
* @param url The URL of the resource to fetch.
|
|
46
|
-
* @param body The request body.
|
|
47
|
-
* @param options The request options.
|
|
48
|
-
* @returns The server response.
|
|
49
|
-
*/
|
|
50
|
-
patch(url, body, options) {
|
|
51
|
-
return this.#fetch(HttpMethod.Patch, url, body, options);
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Performs a POST request.
|
|
55
|
-
* @param url The URL of the resource to fetch.
|
|
56
|
-
* @param body The request body.
|
|
57
|
-
* @param options The request options.
|
|
58
|
-
* @returns The server response.
|
|
59
|
-
*/
|
|
60
|
-
post(url, body, options) {
|
|
61
|
-
return this.#fetch(HttpMethod.Post, url, body, options);
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Performs a PUT request.
|
|
65
|
-
* @param url The URL of the resource to fetch.
|
|
66
|
-
* @param body The request body.
|
|
67
|
-
* @param options The request options.
|
|
68
|
-
* @returns The server response.
|
|
69
|
-
*/
|
|
70
|
-
put(url, body, options) {
|
|
71
|
-
return this.#fetch(HttpMethod.Put, url, body, options);
|
|
72
|
-
}
|
|
73
|
-
/**
|
|
74
|
-
* Performs a custom HTTP request.
|
|
75
|
-
* @param method The HTTP method.
|
|
76
|
-
* @param url The URL of the resource to fetch.
|
|
77
|
-
* @param body The request body.
|
|
78
|
-
* @param options The request options.
|
|
79
|
-
* @returns The server response.
|
|
80
|
-
*/
|
|
81
|
-
async #fetch(method, url = "", body = null, options = {}) {
|
|
82
|
-
const headers = new Headers(options.headers);
|
|
83
|
-
if (!headers.has("Accept"))
|
|
84
|
-
headers.set("Accept", MediaType.Application.Json);
|
|
85
|
-
if (body && !(body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams)) {
|
|
86
|
-
if (typeof body != "string")
|
|
87
|
-
body = JSON.stringify(body);
|
|
88
|
-
if (!headers.has("Content-Type"))
|
|
89
|
-
headers.set("Content-Type", MediaType.Application.Json);
|
|
90
|
-
}
|
|
91
|
-
const loadingIndicator = this.#loadingIndicator();
|
|
92
|
-
try {
|
|
93
|
-
loadingIndicator?.start();
|
|
94
|
-
const request = new Request(new URL(url, this.baseAddress), { ...options, method, headers, body });
|
|
95
|
-
const response = await fetch(request);
|
|
96
|
-
if (!response.ok)
|
|
97
|
-
throw new HttpRequestError(response);
|
|
98
|
-
return response;
|
|
99
|
-
}
|
|
100
|
-
finally {
|
|
101
|
-
loadingIndicator?.stop();
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { StatusCode } from "./StatusCode.js";
|
|
2
|
-
/**
|
|
3
|
-
* An error thrown by the HTTP client.
|
|
4
|
-
*/
|
|
5
|
-
export declare class HttpRequestError extends globalThis.Error {
|
|
6
|
-
#private;
|
|
7
|
-
/**
|
|
8
|
-
* Creates a new HTTP error.
|
|
9
|
-
* @param response The HTTP response.
|
|
10
|
-
*/
|
|
11
|
-
constructor(response: Response);
|
|
12
|
-
/**
|
|
13
|
-
* The HTTP response.
|
|
14
|
-
*/
|
|
15
|
-
get cause(): Response;
|
|
16
|
-
/**
|
|
17
|
-
* Value indicating whether the HTTP status code is between 400 and 499.
|
|
18
|
-
*/
|
|
19
|
-
get isClientError(): boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Value indicating whether the HTTP status code is between 500 and 599.
|
|
22
|
-
*/
|
|
23
|
-
get isServerError(): boolean;
|
|
24
|
-
/**
|
|
25
|
-
* The HTTP status code.
|
|
26
|
-
*/
|
|
27
|
-
get statusCode(): StatusCode;
|
|
28
|
-
/**
|
|
29
|
-
* The validation errors.
|
|
30
|
-
*/
|
|
31
|
-
get validationErrors(): Promise<Map<string, string>>;
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=HttpRequestError.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"HttpRequestError.d.ts","sourceRoot":"","sources":["../../../src/Client/Net/Http/HttpRequestError.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,UAAU,EAAC,MAAM,iBAAiB,CAAC;AAE3C;;GAEG;AACH,qBAAa,gBAAiB,SAAQ,UAAU,CAAC,KAAK;;IAOrD;;;OAGG;gBACS,QAAQ,EAAE,QAAQ;IAK9B;;OAEG;IACH,IAAa,KAAK,IAAI,QAAQ,CAE7B;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAG3B;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAG3B;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,UAAU,CAE3B;IAED;;OAEG;IACH,IAAI,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAInD;CAgBD"}
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
import { StatusCode } from "./StatusCode.js";
|
|
2
|
-
/**
|
|
3
|
-
* An error thrown by the HTTP client.
|
|
4
|
-
*/
|
|
5
|
-
export class HttpRequestError extends globalThis.Error {
|
|
6
|
-
/**
|
|
7
|
-
* The validation errors.
|
|
8
|
-
*/
|
|
9
|
-
#validationErrors = null;
|
|
10
|
-
/**
|
|
11
|
-
* Creates a new HTTP error.
|
|
12
|
-
* @param response The HTTP response.
|
|
13
|
-
*/
|
|
14
|
-
constructor(response) {
|
|
15
|
-
super(`${response.status} ${response.statusText}`, { cause: response });
|
|
16
|
-
this.name = "HttpRequestError";
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* The HTTP response.
|
|
20
|
-
*/
|
|
21
|
-
get cause() {
|
|
22
|
-
return super.cause;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Value indicating whether the HTTP status code is between 400 and 499.
|
|
26
|
-
*/
|
|
27
|
-
get isClientError() {
|
|
28
|
-
const { statusCode } = this;
|
|
29
|
-
return statusCode >= 400 && statusCode < 500;
|
|
30
|
-
}
|
|
31
|
-
/**
|
|
32
|
-
* Value indicating whether the HTTP status code is between 500 and 599.
|
|
33
|
-
*/
|
|
34
|
-
get isServerError() {
|
|
35
|
-
const { statusCode } = this;
|
|
36
|
-
return statusCode >= 500 && statusCode < 600;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* The HTTP status code.
|
|
40
|
-
*/
|
|
41
|
-
get statusCode() {
|
|
42
|
-
return this.cause.status;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* The validation errors.
|
|
46
|
-
*/
|
|
47
|
-
get validationErrors() {
|
|
48
|
-
return this.#validationErrors
|
|
49
|
-
? Promise.resolve(this.#validationErrors)
|
|
50
|
-
: this.#parseValidationErrors().then(errors => this.#validationErrors = errors);
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Parses the validation errors returned in the body of the specified response.
|
|
54
|
-
* @returns The validation errors provided by the response body.
|
|
55
|
-
*/
|
|
56
|
-
async #parseValidationErrors() {
|
|
57
|
-
try {
|
|
58
|
-
const statuses = [StatusCode.BadRequest, StatusCode.UnprocessableContent];
|
|
59
|
-
const ignoreBody = this.cause.bodyUsed || !statuses.includes(this.statusCode);
|
|
60
|
-
return new Map(ignoreBody ? [] : Object.entries(await this.cause.json()));
|
|
61
|
-
}
|
|
62
|
-
catch {
|
|
63
|
-
return new Map;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
}
|
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
import {MediaType} from "../Mime/MediaType.js";
|
|
2
|
-
import {HttpMethod} from "./HttpMethod.js";
|
|
3
|
-
import {HttpRequestError} from "./HttpRequestError.js";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Performs HTTP requests.
|
|
7
|
-
*/
|
|
8
|
-
export class HttpClient {
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* The base URL of the remote service.
|
|
12
|
-
*/
|
|
13
|
-
readonly baseAddress: URL;
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* The function returning the component used as loading indicator.
|
|
17
|
-
*/
|
|
18
|
-
readonly #loadingIndicator: () => ILoadingIndicator|null;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Creates a new HTTP client.
|
|
22
|
-
* @param options An object providing values to initialize this instance.
|
|
23
|
-
*/
|
|
24
|
-
constructor(options: HttpClientOptions = {}) {
|
|
25
|
-
const url = options.baseUrl ? (options.baseUrl instanceof URL ? options.baseUrl.href : options.baseUrl) : document.baseURI;
|
|
26
|
-
this.baseAddress = new URL(url.endsWith("/") ? url : `${url}/`);
|
|
27
|
-
this.#loadingIndicator = options.loadingIndicator ?? (() => document.body.querySelector("loading-indicator, .loading-indicator") as ILoadingIndicator|null);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Performs a DELETE request.
|
|
32
|
-
* @param url The URL of the resource to fetch.
|
|
33
|
-
* @param options The request options.
|
|
34
|
-
* @returns The server response.
|
|
35
|
-
*/
|
|
36
|
-
delete(url?: string|URL, options?: RequestInit): Promise<Response> {
|
|
37
|
-
return this.#fetch(HttpMethod.Delete, url, null, options);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Performs a GET request.
|
|
42
|
-
* @param url The URL of the resource to fetch.
|
|
43
|
-
* @param options The request options.
|
|
44
|
-
* @returns The server response.
|
|
45
|
-
*/
|
|
46
|
-
get(url?: string|URL, options?: RequestInit): Promise<Response> {
|
|
47
|
-
return this.#fetch(HttpMethod.Get, url, null, options);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Performs a PATCH request.
|
|
52
|
-
* @param url The URL of the resource to fetch.
|
|
53
|
-
* @param body The request body.
|
|
54
|
-
* @param options The request options.
|
|
55
|
-
* @returns The server response.
|
|
56
|
-
*/
|
|
57
|
-
patch(url?: string|URL, body?: unknown, options?: RequestInit): Promise<Response> {
|
|
58
|
-
return this.#fetch(HttpMethod.Patch, url, body, options);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Performs a POST request.
|
|
63
|
-
* @param url The URL of the resource to fetch.
|
|
64
|
-
* @param body The request body.
|
|
65
|
-
* @param options The request options.
|
|
66
|
-
* @returns The server response.
|
|
67
|
-
*/
|
|
68
|
-
post(url?: string|URL, body?: unknown, options?: RequestInit): Promise<Response> {
|
|
69
|
-
return this.#fetch(HttpMethod.Post, url, body, options);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Performs a PUT request.
|
|
74
|
-
* @param url The URL of the resource to fetch.
|
|
75
|
-
* @param body The request body.
|
|
76
|
-
* @param options The request options.
|
|
77
|
-
* @returns The server response.
|
|
78
|
-
*/
|
|
79
|
-
put(url?: string|URL, body?: unknown, options?: RequestInit): Promise<Response> {
|
|
80
|
-
return this.#fetch(HttpMethod.Put, url, body, options);
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Performs a custom HTTP request.
|
|
85
|
-
* @param method The HTTP method.
|
|
86
|
-
* @param url The URL of the resource to fetch.
|
|
87
|
-
* @param body The request body.
|
|
88
|
-
* @param options The request options.
|
|
89
|
-
* @returns The server response.
|
|
90
|
-
*/
|
|
91
|
-
async #fetch(method: string, url: string|URL = "", body: unknown = null, options: RequestInit = {}): Promise<Response> {
|
|
92
|
-
const headers = new Headers(options.headers);
|
|
93
|
-
if (!headers.has("Accept")) headers.set("Accept", MediaType.Application.Json);
|
|
94
|
-
|
|
95
|
-
if (body && !(body instanceof Blob || body instanceof FormData || body instanceof URLSearchParams)) {
|
|
96
|
-
if (typeof body != "string") body = JSON.stringify(body);
|
|
97
|
-
if (!headers.has("Content-Type")) headers.set("Content-Type", MediaType.Application.Json);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const loadingIndicator = this.#loadingIndicator();
|
|
101
|
-
try {
|
|
102
|
-
loadingIndicator?.start();
|
|
103
|
-
const request = new Request(new URL(url, this.baseAddress), {...options, method, headers, body} as RequestInit);
|
|
104
|
-
const response = await fetch(request);
|
|
105
|
-
if (!response.ok) throw new HttpRequestError(response);
|
|
106
|
-
return response;
|
|
107
|
-
}
|
|
108
|
-
finally {
|
|
109
|
-
loadingIndicator?.stop();
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* Defines the options of a {@link HttpClient} instance.
|
|
116
|
-
*/
|
|
117
|
-
export type HttpClientOptions = Partial<{
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* The base URL of the remote service.
|
|
121
|
-
*/
|
|
122
|
-
baseUrl: string|URL;
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* The function returning the component used as loading indicator.
|
|
126
|
-
*/
|
|
127
|
-
loadingIndicator: () => ILoadingIndicator|null;
|
|
128
|
-
}>;
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* A component that shows up when an HTTP request starts, and hides when all concurrent HTTP requests are completed.
|
|
132
|
-
*/
|
|
133
|
-
export interface ILoadingIndicator {
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Starts the loading indicator.
|
|
137
|
-
*/
|
|
138
|
-
start: () => void;
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Stops the loading indicator.
|
|
142
|
-
* @param options Value indicating whether to force the loading indicator to stop.
|
|
143
|
-
*/
|
|
144
|
-
stop: (options?: {force?: boolean}) => void;
|
|
145
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import {StatusCode} from "./StatusCode.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* An error thrown by the HTTP client.
|
|
5
|
-
*/
|
|
6
|
-
export class HttpRequestError extends globalThis.Error {
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* The validation errors.
|
|
10
|
-
*/
|
|
11
|
-
#validationErrors: Map<string, string>|null = null;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Creates a new HTTP error.
|
|
15
|
-
* @param response The HTTP response.
|
|
16
|
-
*/
|
|
17
|
-
constructor(response: Response) {
|
|
18
|
-
super(`${response.status} ${response.statusText}`, {cause: response});
|
|
19
|
-
this.name = "HttpRequestError";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* The HTTP response.
|
|
24
|
-
*/
|
|
25
|
-
override get cause(): Response {
|
|
26
|
-
return super.cause as Response;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Value indicating whether the HTTP status code is between 400 and 499.
|
|
31
|
-
*/
|
|
32
|
-
get isClientError(): boolean {
|
|
33
|
-
const {statusCode} = this;
|
|
34
|
-
return statusCode >= 400 && statusCode < 500;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Value indicating whether the HTTP status code is between 500 and 599.
|
|
39
|
-
*/
|
|
40
|
-
get isServerError(): boolean {
|
|
41
|
-
const {statusCode} = this;
|
|
42
|
-
return statusCode >= 500 && statusCode < 600;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* The HTTP status code.
|
|
47
|
-
*/
|
|
48
|
-
get statusCode(): StatusCode {
|
|
49
|
-
return this.cause.status as StatusCode;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* The validation errors.
|
|
54
|
-
*/
|
|
55
|
-
get validationErrors(): Promise<Map<string, string>> {
|
|
56
|
-
return this.#validationErrors
|
|
57
|
-
? Promise.resolve(this.#validationErrors)
|
|
58
|
-
: this.#parseValidationErrors().then(errors => this.#validationErrors = errors);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Parses the validation errors returned in the body of the specified response.
|
|
63
|
-
* @returns The validation errors provided by the response body.
|
|
64
|
-
*/
|
|
65
|
-
async #parseValidationErrors(): Promise<Map<string, string>> {
|
|
66
|
-
try {
|
|
67
|
-
const statuses: StatusCode[] = [StatusCode.BadRequest, StatusCode.UnprocessableContent];
|
|
68
|
-
const ignoreBody = this.cause.bodyUsed || !statuses.includes(this.statusCode);
|
|
69
|
-
return new Map(ignoreBody ? [] : Object.entries(await this.cause.json() as Record<string, string>));
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
return new Map;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|