@equinor/fusion-framework-module-http 0.0.0-next-20230915111233

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.
Files changed (87) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/CHANGELOG.md +427 -0
  3. package/LICENSE +21 -0
  4. package/README.md +57 -0
  5. package/dist/esm/configurator.js +22 -0
  6. package/dist/esm/configurator.js.map +1 -0
  7. package/dist/esm/errors.js +8 -0
  8. package/dist/esm/errors.js.map +1 -0
  9. package/dist/esm/index.js +6 -0
  10. package/dist/esm/index.js.map +1 -0
  11. package/dist/esm/lib/client/client-msal.js +15 -0
  12. package/dist/esm/lib/client/client-msal.js.map +1 -0
  13. package/dist/esm/lib/client/client.js +108 -0
  14. package/dist/esm/lib/client/client.js.map +1 -0
  15. package/dist/esm/lib/client/index.js +4 -0
  16. package/dist/esm/lib/client/index.js.map +1 -0
  17. package/dist/esm/lib/client/types.js +2 -0
  18. package/dist/esm/lib/client/types.js.map +1 -0
  19. package/dist/esm/lib/index.js +4 -0
  20. package/dist/esm/lib/index.js.map +1 -0
  21. package/dist/esm/lib/operators/http-request-handler.js +10 -0
  22. package/dist/esm/lib/operators/http-request-handler.js.map +1 -0
  23. package/dist/esm/lib/operators/http-response-handler.js +5 -0
  24. package/dist/esm/lib/operators/http-response-handler.js.map +1 -0
  25. package/dist/esm/lib/operators/index.js +5 -0
  26. package/dist/esm/lib/operators/index.js.map +1 -0
  27. package/dist/esm/lib/operators/process-operators.js +27 -0
  28. package/dist/esm/lib/operators/process-operators.js.map +1 -0
  29. package/dist/esm/lib/operators/request-operator-header.js +7 -0
  30. package/dist/esm/lib/operators/request-operator-header.js.map +1 -0
  31. package/dist/esm/lib/operators/types.js +2 -0
  32. package/dist/esm/lib/operators/types.js.map +1 -0
  33. package/dist/esm/lib/selectors/blob-selector.js +16 -0
  34. package/dist/esm/lib/selectors/blob-selector.js.map +1 -0
  35. package/dist/esm/lib/selectors/index.js +3 -0
  36. package/dist/esm/lib/selectors/index.js.map +1 -0
  37. package/dist/esm/lib/selectors/json-selector.js +16 -0
  38. package/dist/esm/lib/selectors/json-selector.js.map +1 -0
  39. package/dist/esm/module.js +48 -0
  40. package/dist/esm/module.js.map +1 -0
  41. package/dist/esm/provider.js +51 -0
  42. package/dist/esm/provider.js.map +1 -0
  43. package/dist/esm/version.js +2 -0
  44. package/dist/esm/version.js.map +1 -0
  45. package/dist/tsconfig.tsbuildinfo +1 -0
  46. package/dist/types/configurator.d.ts +37 -0
  47. package/dist/types/errors.d.ts +5 -0
  48. package/dist/types/index.d.ts +6 -0
  49. package/dist/types/lib/client/client-msal.d.ts +12 -0
  50. package/dist/types/lib/client/client.d.ts +34 -0
  51. package/dist/types/lib/client/index.d.ts +3 -0
  52. package/dist/types/lib/client/types.d.ts +36 -0
  53. package/dist/types/lib/index.d.ts +3 -0
  54. package/dist/types/lib/operators/http-request-handler.d.ts +6 -0
  55. package/dist/types/lib/operators/http-response-handler.d.ts +4 -0
  56. package/dist/types/lib/operators/index.d.ts +4 -0
  57. package/dist/types/lib/operators/process-operators.d.ts +9 -0
  58. package/dist/types/lib/operators/request-operator-header.d.ts +4 -0
  59. package/dist/types/lib/operators/types.d.ts +14 -0
  60. package/dist/types/lib/selectors/blob-selector.d.ts +2 -0
  61. package/dist/types/lib/selectors/index.d.ts +2 -0
  62. package/dist/types/lib/selectors/json-selector.d.ts +2 -0
  63. package/dist/types/module.d.ts +18 -0
  64. package/dist/types/provider.d.ts +23 -0
  65. package/dist/types/version.d.ts +1 -0
  66. package/package.json +51 -0
  67. package/src/configurator.ts +131 -0
  68. package/src/errors.ts +9 -0
  69. package/src/index.ts +14 -0
  70. package/src/lib/client/client-msal.ts +33 -0
  71. package/src/lib/client/client.ts +204 -0
  72. package/src/lib/client/index.ts +4 -0
  73. package/src/lib/client/types.ts +168 -0
  74. package/src/lib/index.ts +3 -0
  75. package/src/lib/operators/http-request-handler.ts +21 -0
  76. package/src/lib/operators/http-response-handler.ts +5 -0
  77. package/src/lib/operators/index.ts +5 -0
  78. package/src/lib/operators/process-operators.ts +43 -0
  79. package/src/lib/operators/request-operator-header.ts +12 -0
  80. package/src/lib/operators/types.ts +42 -0
  81. package/src/lib/selectors/blob-selector.ts +19 -0
  82. package/src/lib/selectors/index.ts +2 -0
  83. package/src/lib/selectors/json-selector.ts +19 -0
  84. package/src/module.ts +80 -0
  85. package/src/provider.ts +98 -0
  86. package/src/version.ts +2 -0
  87. package/tsconfig.json +24 -0
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@equinor/fusion-framework-module-http",
3
+ "version": "0.0.0-next-20230915111233",
4
+ "description": "",
5
+ "main": "dist/esm/index.js",
6
+ "types": "index.d.ts",
7
+ "exports": {
8
+ ".": "./dist/esm/index.js",
9
+ "./client": "./dist/esm/lib/client/index.js",
10
+ "./operators": "./dist/esm/lib/operators/index.js",
11
+ "./selectors": "./dist/esm/lib/selectors/index.js"
12
+ },
13
+ "typesVersions": {
14
+ ">=4.2": {
15
+ "*": [
16
+ "dist/types/*"
17
+ ],
18
+ "client": [
19
+ "dist/types/lib/client/index.d.ts"
20
+ ],
21
+ "operators": [
22
+ "dist/types/lib/operators/index.d.ts"
23
+ ],
24
+ "selectors": [
25
+ "dist/types/lib/selectors/index.d.ts"
26
+ ]
27
+ }
28
+ },
29
+ "keywords": [],
30
+ "author": "",
31
+ "license": "ISC",
32
+ "publishConfig": {
33
+ "access": "public"
34
+ },
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "git+https://github.com/equinor/fusion-framework.git",
38
+ "directory": "packages/modules/http"
39
+ },
40
+ "dependencies": {
41
+ "rxjs": "^7.8.1",
42
+ "@equinor/fusion-framework-module": "^4.2.4",
43
+ "@equinor/fusion-framework-module-msal": "^3.0.6"
44
+ },
45
+ "devDependencies": {
46
+ "typescript": "^5.1.3"
47
+ },
48
+ "scripts": {
49
+ "build": "tsc -b"
50
+ }
51
+ }
@@ -0,0 +1,131 @@
1
+ import { HttpRequestHandler } from './lib/operators';
2
+
3
+ import type { FetchRequest, IHttpClient } from './lib/client';
4
+ import type { IHttpRequestHandler, IHttpResponseHandler } from './lib/operators';
5
+
6
+ interface HttpClientConstructorOptions<TInit extends FetchRequest> {
7
+ requestHandler: IHttpRequestHandler<TInit>;
8
+ }
9
+
10
+ interface HttpClientConstructor<TClient extends IHttpClient> {
11
+ new (
12
+ uri: string,
13
+ options: HttpClientConstructorOptions<HttpClientRequestInitType<TClient>>,
14
+ ): TClient;
15
+ }
16
+
17
+ export interface HttpClientOptions<TClient extends IHttpClient = IHttpClient> {
18
+ baseUri?: string;
19
+ defaultScopes?: string[];
20
+ ctor?: HttpClientConstructor<TClient>;
21
+ onCreate?: (client: TClient) => void;
22
+ requestHandler?: IHttpRequestHandler<HttpClientRequestInitType<TClient>>;
23
+ responseHandler?: IHttpResponseHandler<HttpClientRequestInitType<TClient>>;
24
+ }
25
+
26
+ export type HttpClientRequestInitType<T extends IHttpClient> = T extends IHttpClient<infer U>
27
+ ? U
28
+ : never;
29
+
30
+ /**
31
+ * Instance for configuring http client
32
+ * @template TClient base type of client that the provider will create
33
+ */
34
+ export interface IHttpClientConfigurator<TClient extends IHttpClient = IHttpClient> {
35
+ readonly clients: Record<string, HttpClientOptions<TClient>>;
36
+ readonly defaultHttpClientCtor: HttpClientConstructor<TClient>;
37
+ readonly defaultHttpRequestHandler: IHttpRequestHandler<HttpClientRequestInitType<TClient>>;
38
+
39
+ /**
40
+ * Configure a client with arguments
41
+ * @param name name of the client
42
+ * @param args option that are used cor creating a client
43
+ * @example
44
+ * ```ts
45
+ * configurator.http.configureClient('foo',{
46
+ * baseUri: 'https://foo.bar',
47
+ * defaultScopes: ['foobar/.default']
48
+ * });
49
+ * ```
50
+ */
51
+ configureClient<T extends TClient>(
52
+ name: string,
53
+ args: HttpClientOptions<T>,
54
+ ): IHttpClientConfigurator<TClient>;
55
+
56
+ /**
57
+ * Configure a simple client by name to an endpoint
58
+ * @param name name of the client
59
+ * @param uri base endpoint for the client
60
+ */
61
+ configureClient(name: string, uri: string): IHttpClientConfigurator<TClient>;
62
+
63
+ /**
64
+ * Creates a client with callback configuration
65
+ * @param name name of the client
66
+ * @param onCreate callback when a client is created
67
+ * ```ts
68
+ * configurator.http.configureClient('foo',(client) => {
69
+ * client.requestHandler.add('logger', (request) => console.log(request));
70
+ * });
71
+ * ```
72
+ */
73
+ configureClient<T extends TClient>(
74
+ name: string,
75
+ onCreate: (client: T) => void,
76
+ ): HttpClientConfigurator<TClient>;
77
+
78
+ /**
79
+ * Check if there is a configuration for provided name
80
+ */
81
+ hasClient(name: string): boolean;
82
+ }
83
+
84
+ /** @inheritdoc */
85
+ export class HttpClientConfigurator<TClient extends IHttpClient>
86
+ implements IHttpClientConfigurator<TClient>
87
+ {
88
+ protected _clients: Record<string, HttpClientOptions<TClient>> = {};
89
+
90
+ /** Get a clone of all configured clients */
91
+ public get clients(): Record<string, HttpClientOptions<TClient>> {
92
+ return { ...this._clients };
93
+ }
94
+
95
+ /** default class for creation of http clients */
96
+ readonly defaultHttpClientCtor: HttpClientConstructor<TClient>;
97
+
98
+ /** default request handler for http clients, applied on creation */
99
+ readonly defaultHttpRequestHandler = new HttpRequestHandler<
100
+ HttpClientRequestInitType<TClient>
101
+ >();
102
+
103
+ /**
104
+ * Create a instance of http configuration
105
+ * @param client defaultHttpRequestHandler
106
+ */
107
+ constructor(client: HttpClientConstructor<TClient>) {
108
+ this.defaultHttpClientCtor = client;
109
+ }
110
+
111
+ /** @inheritdoc */
112
+ hasClient(name: string): boolean {
113
+ return Object.keys(this._clients).includes(name);
114
+ }
115
+
116
+ /** @inheritdoc */
117
+ configureClient<T extends TClient>(
118
+ name: string,
119
+ args: string | HttpClientOptions<T> | HttpClientOptions<T>['onCreate'],
120
+ ): HttpClientConfigurator<TClient> {
121
+ const argFn = typeof args === 'string' ? (x: T) => (x.uri = String(args)) : args;
122
+ const options = typeof argFn === 'function' ? { onCreate: argFn } : argFn;
123
+ this._clients[name] = {
124
+ ...this._clients[name],
125
+ ...(options as unknown as HttpClientOptions<TClient>),
126
+ };
127
+ return this;
128
+ }
129
+ }
130
+
131
+ export default HttpClientConfigurator;
package/src/errors.ts ADDED
@@ -0,0 +1,9 @@
1
+ export class HttpResponseError extends Error {
2
+ constructor(
3
+ public message: string,
4
+ public response: Response,
5
+ options?: ErrorOptions,
6
+ ) {
7
+ super(message, options);
8
+ }
9
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * [[include:module-http/README.MD]]
3
+ * @module
4
+ */
5
+
6
+ export * from './configurator';
7
+ export * from './provider';
8
+ export * from './module';
9
+
10
+ export * from './errors';
11
+
12
+ export type { IHttpClient, FetchResponse } from './lib/client';
13
+
14
+ export { default } from './module';
@@ -0,0 +1,33 @@
1
+ import type { Observable } from 'rxjs';
2
+ import type { FetchRequestInit, FetchRequest, FetchResponse } from '.';
3
+ import { HttpClient } from './client';
4
+
5
+ /** Extends request init with scope */
6
+ type MsalFetchRequest = FetchRequest & { scopes?: string[] };
7
+ type MsalFetchRequestInit<
8
+ TReturn = unknown,
9
+ TRequest = FetchRequest,
10
+ TResponse = FetchResponse,
11
+ > = FetchRequestInit<TReturn, TRequest, TResponse> & Pick<MsalFetchRequest, 'scopes'>;
12
+
13
+ /** Default Client for MSAL */
14
+ export class HttpClientMsal<
15
+ TRequest extends MsalFetchRequest = MsalFetchRequest,
16
+ TResponse extends FetchResponse = FetchResponse,
17
+ > extends HttpClient<TRequest, TResponse> {
18
+ /** Scope that will be applied to all request if no scope is provided in request object */
19
+ public defaultScopes: string[] = [];
20
+
21
+ /** @inheritdoc */
22
+ fetch$<T = TResponse>(
23
+ path: string,
24
+ init?: MsalFetchRequestInit<T, TRequest, TResponse>,
25
+ ): Observable<T> {
26
+ const args = Object.assign(init || {}, {
27
+ scopes: this.defaultScopes.concat(init?.scopes || []),
28
+ }) as FetchRequestInit<T, TRequest, TResponse>;
29
+ return super._fetch$(path, args);
30
+ }
31
+ }
32
+
33
+ export default HttpClientMsal;
@@ -0,0 +1,204 @@
1
+ import { firstValueFrom, of, Subject } from 'rxjs';
2
+ import { switchMap, takeUntil, tap } from 'rxjs/operators';
3
+ import { fromFetch } from 'rxjs/fetch';
4
+
5
+ import { HttpRequestHandler, HttpResponseHandler } from '../operators';
6
+ import { blobSelector, jsonSelector } from '../selectors';
7
+
8
+ import type { Observable, ObservableInput } from 'rxjs';
9
+ import type { IHttpRequestHandler, IHttpResponseHandler } from '../operators';
10
+ import type {
11
+ FetchRequest,
12
+ FetchRequestInit,
13
+ FetchResponse,
14
+ IHttpClient,
15
+ JsonRequest,
16
+ StreamResponse,
17
+ } from '.';
18
+ import { HttpResponseError } from '../../errors';
19
+
20
+ export type HttpClientCreateOptions<
21
+ TRequest extends FetchRequest = FetchRequest,
22
+ TResponse = Response,
23
+ > = {
24
+ requestHandler: IHttpRequestHandler<TRequest>;
25
+ responseHandler: IHttpResponseHandler<TResponse>;
26
+ };
27
+
28
+ /** Base http client for executing requests */
29
+ export class HttpClient<
30
+ TRequest extends FetchRequest = FetchRequest,
31
+ TResponse extends FetchResponse = FetchResponse,
32
+ > implements IHttpClient<TRequest, TResponse>
33
+ {
34
+ readonly requestHandler: IHttpRequestHandler<TRequest>;
35
+
36
+ readonly responseHandler: IHttpResponseHandler<TResponse>;
37
+
38
+ /** stream of requests that are about to be executed */
39
+ protected _request$ = new Subject<TRequest>();
40
+
41
+ /** stream of request responses */
42
+ protected _response$ = new Subject<TResponse>();
43
+
44
+ protected _abort$ = new Subject<void>();
45
+
46
+ public get request$(): Observable<TRequest> {
47
+ return this._request$.asObservable();
48
+ }
49
+
50
+ public get response$(): Observable<TResponse> {
51
+ return this._response$.asObservable();
52
+ }
53
+
54
+ constructor(
55
+ public uri: string,
56
+ options?: Partial<HttpClientCreateOptions<TRequest, TResponse>>,
57
+ ) {
58
+ this.requestHandler = options?.requestHandler ?? new HttpRequestHandler<TRequest>();
59
+ this.responseHandler = options?.responseHandler ?? new HttpResponseHandler<TResponse>();
60
+ this._init();
61
+ }
62
+
63
+ /** internal abstract method, for overriding instead of overriding constructor */
64
+ protected _init(): void {
65
+ // called by children for constructor setup
66
+ }
67
+
68
+ public fetch$<T = TResponse>(
69
+ path: string,
70
+ args?: FetchRequestInit<T, TRequest, TResponse>,
71
+ ): StreamResponse<T> {
72
+ return this._fetch$(path, args);
73
+ }
74
+
75
+ public fetch<T = TResponse>(
76
+ path: string,
77
+ args?: FetchRequestInit<T, TRequest, TResponse>,
78
+ ): Promise<T> {
79
+ return firstValueFrom(this.fetch$<T>(path, args));
80
+ }
81
+
82
+ /** @deprecated */
83
+ public fetchAsync<T = TResponse>(
84
+ path: string,
85
+ args?: FetchRequestInit<T, TRequest, TResponse>,
86
+ ): Promise<T> {
87
+ return this.fetch(path, args);
88
+ }
89
+
90
+ public json$<T = unknown>(
91
+ path: string,
92
+ args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
93
+ ): StreamResponse<T> {
94
+ const body = typeof args?.body === 'object' ? JSON.stringify(args?.body) : args?.body;
95
+ const selector = args?.selector ?? jsonSelector;
96
+ const headers = new Headers(args?.headers);
97
+ headers.append('Accept', 'application/json');
98
+ headers.append('Content-Type', 'application/json');
99
+ return this.fetch$(path, {
100
+ ...args,
101
+ body,
102
+ selector,
103
+ headers,
104
+ } as FetchRequestInit<T, TRequest, TResponse>);
105
+ }
106
+
107
+ public json<T = unknown>(
108
+ path: string,
109
+ args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
110
+ ): Promise<T> {
111
+ return firstValueFrom(this.json$<T>(path, args));
112
+ }
113
+
114
+ public blob$(
115
+ path: string,
116
+ args?: FetchRequestInit<Blob, TRequest, TResponse>,
117
+ ): StreamResponse<Blob> {
118
+ const selector = args?.selector ?? blobSelector;
119
+ return this.fetch$(path, {
120
+ ...args,
121
+ selector,
122
+ } as FetchRequestInit<Blob, TRequest, TResponse>);
123
+ }
124
+
125
+ public blob(path: string, args?: FetchRequestInit<Blob, TRequest, TResponse>): Promise<Blob> {
126
+ return firstValueFrom(this.blob$(path, args));
127
+ }
128
+
129
+ /** @deprecated */
130
+ public jsonAsync<T = unknown>(
131
+ path: string,
132
+ args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
133
+ ): Promise<T> {
134
+ return this.json(path, args);
135
+ }
136
+
137
+ public execute<T = TResponse, TMethod extends 'fetch' | 'fetch$' | 'json' | 'json$' = 'fetch'>(
138
+ method: TMethod,
139
+ path: string,
140
+ init?: FetchRequestInit<T, TRequest, TResponse>,
141
+ ): ReturnType<IHttpClient[TMethod]> {
142
+ return this[method](path, init) as ReturnType<IHttpClient[TMethod]>;
143
+ }
144
+
145
+ public abort(): void {
146
+ this._abort$.next();
147
+ }
148
+
149
+ protected _fetch$<T = TResponse>(
150
+ path: string,
151
+ args?: FetchRequestInit<T, TRequest, TResponse>,
152
+ ): Observable<T> {
153
+ const { selector, ...options } = args || {};
154
+ const response$ = of({
155
+ ...options,
156
+ uri: this._resolveUrl(path),
157
+ } as TRequest).pipe(
158
+ /** prepare request, allow extensions to modify request */
159
+ switchMap((x) => this._prepareRequest(x)),
160
+ /** push request to event buss */
161
+ tap((x) => this._request$.next(x)),
162
+ /** execute request */
163
+ switchMap(({ uri, path: _path, ...init }) => fromFetch(uri, init)),
164
+ /** prepare response, allow extensions to modify response */
165
+ switchMap((x) => this._prepareResponse(x as unknown as TResponse)),
166
+ /** push response to event buss */
167
+ tap((x) => this._response$.next(x)),
168
+
169
+ /** execute selector */
170
+ switchMap((response) => {
171
+ if (selector) {
172
+ try {
173
+ return selector(response);
174
+ } catch (err) {
175
+ throw new HttpResponseError(
176
+ 'failed to execute response selector',
177
+ response as Response,
178
+ {
179
+ cause: err,
180
+ },
181
+ );
182
+ }
183
+ }
184
+ return of(response);
185
+ }),
186
+ /** cancel request on abort signal */
187
+ takeUntil(this._abort$),
188
+ );
189
+ return response$ as unknown as Observable<T>;
190
+ }
191
+
192
+ protected _prepareRequest(init: TRequest): ObservableInput<TRequest> {
193
+ return this.requestHandler.process(init);
194
+ }
195
+
196
+ protected _prepareResponse(response: TResponse): ObservableInput<TResponse> {
197
+ return this.responseHandler.process(response);
198
+ }
199
+
200
+ protected _resolveUrl(path: string): string {
201
+ const baseUrl = this.uri || window.location.origin;
202
+ return new URL(path, baseUrl).href;
203
+ }
204
+ }
@@ -0,0 +1,4 @@
1
+ export { HttpClient } from './client';
2
+ export { HttpClientMsal } from './client-msal';
3
+
4
+ export * from './types';
@@ -0,0 +1,168 @@
1
+ import type { ObservableInput, Observable } from 'rxjs';
2
+ import type { IHttpRequestHandler, IHttpResponseHandler } from '../operators/types';
3
+
4
+ export type StreamResponse<T> = Observable<T>;
5
+
6
+ export type FetchRequest = RequestInit & {
7
+ uri: string;
8
+ path: string;
9
+ };
10
+
11
+ export type JsonRequest<TRequest extends FetchRequest = FetchRequest> = Omit<TRequest, 'body'> & {
12
+ body?: object | string | null;
13
+ };
14
+
15
+ export type FetchResponse<T = unknown> = Response & {
16
+ json(): Promise<T>;
17
+ };
18
+
19
+ export type FetchRequestInit<
20
+ TReturn = unknown,
21
+ TRequest = FetchRequest,
22
+ TResponse = FetchResponse<TReturn>,
23
+ > = Omit<TRequest, 'uri' | 'path'> & {
24
+ selector?: (response: TResponse) => ObservableInput<TReturn>;
25
+ };
26
+
27
+ export type ClientRequestInit<T extends IHttpClient, TReturn = unknown> = T extends IHttpClient<
28
+ infer TRequest,
29
+ infer TResponse
30
+ >
31
+ ? FetchRequestInit<TReturn, TRequest, TResponse>
32
+ : never;
33
+
34
+ export type ExecutionMethod = 'fetch' | 'fetch$' | 'json' | 'json$';
35
+
36
+ export type ExecutionMethodParameters<
37
+ TMethod extends ExecutionMethod = 'fetch',
38
+ TClient extends IHttpClient = IHttpClient,
39
+ > = Parameters<TClient[TMethod]>;
40
+
41
+ export type ExecutionResponse<
42
+ TMethod extends ExecutionMethod = 'fetch',
43
+ TClient extends IHttpClient = IHttpClient,
44
+ > = ReturnType<TClient[TMethod]>;
45
+
46
+ /**
47
+ * @template TRequest request arguments @see {@link https://developer.mozilla.org/en-US/docs/Web/API/request|request}
48
+ * @template TResponse request arguments @see {@link https://developer.mozilla.org/en-US/docs/Web/API/response|response}
49
+ */
50
+ export interface IHttpClient<TRequest extends FetchRequest = FetchRequest, TResponse = Response> {
51
+ uri: string;
52
+ /** pre-processor of requests */
53
+ readonly requestHandler: IHttpRequestHandler<TRequest>;
54
+ /** post-processor of requests */
55
+ readonly responseHandler: IHttpResponseHandler<TResponse>;
56
+
57
+ /** Observable stream of request */
58
+ request$: Observable<TRequest>;
59
+
60
+ /** Observable stream of responses */
61
+ response$: Observable<TResponse>;
62
+
63
+ /**
64
+ * Observable request.
65
+ * Simplifies execution of request and
66
+ * note: request will not be executed until subscribe!
67
+ *
68
+ * @see {@link https://rxjs.dev/api/fetch/fromFetch|RXJS}
69
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch|fetch}
70
+ * @example
71
+ * ```ts
72
+ * // Observer changes of a input field
73
+ * const client = modules.http.createClient('my-client');
74
+ * const input$ = fromEvent(document.getElementById('input'), 'input');
75
+ * input$.pipe(
76
+ * // only call after no key input in .5s
77
+ * debounceTime(500),
78
+ * // extract value from event
79
+ * map(x => x.currentTarget.value),
80
+ * // only search when text longer than 2 characters
81
+ * filter(x => x.length >=3),
82
+ * // query api with input value
83
+ * switchMap(x => client.fetch(`api/foo?q=${x}`).pipe(
84
+ * // retry 2 times
85
+ * retry(2)
86
+ * // cancel request if new input
87
+ * takeUntil(input$)
88
+ * )),
89
+ * // extract data from response
90
+ * switchMap(x => x.json()),
91
+ * // process error
92
+ * catchError(x => of({error: e.message}))
93
+ * // write result to pre element
94
+ * ).subscribe(console.log);
95
+ * ```
96
+ */
97
+ fetch$<T = TResponse>(
98
+ path: string,
99
+ init?: FetchRequestInit<T, TRequest, TResponse>,
100
+ ): StreamResponse<T>;
101
+
102
+ /**
103
+ * Fetch a resource as an promise
104
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch|fetch}
105
+ * @example
106
+ * ```ts
107
+ * let controller: AbortController;
108
+ * const client = window.Fusion.createClient('my-client');
109
+ * const input = document.getElementById('input');
110
+ * input.addEventlistener('input', (e) => {
111
+ * try{
112
+ * // if a controller is defined, request might be ongoing
113
+ * controller && controller.abort();
114
+ * // create a new abort controller
115
+ * controller = new AbortController();
116
+ * // query api with
117
+ * const response = await client.fetch({
118
+ * path: `api/foo?q=${e.currentTarget.value}`,
119
+ * signal: controller.signal,
120
+ * });
121
+ * const json = await response.json();
122
+ * result.innerText = JSON.stringify(json, null, 2)
123
+ * } catch(err){
124
+ * result.innerText = 'an error occurred'
125
+ * } finally{
126
+ * delete controller;
127
+ * }
128
+ * });
129
+ * ```
130
+ */
131
+ fetch<T = TResponse>(path: string, init?: FetchRequestInit<T, TRequest, TResponse>): Promise<T>;
132
+
133
+ fetchAsync<T = TResponse>(
134
+ path: string,
135
+ args?: FetchRequestInit<T, TRequest, TResponse>,
136
+ ): Promise<T>;
137
+
138
+ json$<T = unknown>(
139
+ path: string,
140
+ init?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
141
+ ): StreamResponse<T>;
142
+
143
+ json<T = unknown>(
144
+ path: string,
145
+ init?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
146
+ ): Promise<T>;
147
+
148
+ /** @deprecated */
149
+ jsonAsync<T = unknown>(
150
+ path: string,
151
+ args?: FetchRequestInit<T, JsonRequest<TRequest>, TResponse>,
152
+ ): Promise<T>;
153
+
154
+ blob(
155
+ path: string,
156
+ args?: FetchRequestInit<Blob, JsonRequest<TRequest>, TResponse>,
157
+ ): Promise<Blob>;
158
+
159
+ blob$(
160
+ path: string,
161
+ args?: FetchRequestInit<Blob, JsonRequest<TRequest>, TResponse>,
162
+ ): StreamResponse<Blob>;
163
+
164
+ /**
165
+ * Abort all ongoing request for current client
166
+ */
167
+ abort(): void;
168
+ }
@@ -0,0 +1,3 @@
1
+ export * from './client';
2
+ export * from './operators';
3
+ export * from './selectors';
@@ -0,0 +1,21 @@
1
+ import { ProcessOperators } from './process-operators';
2
+ import { requestOperatorHeader } from './request-operator-header';
3
+
4
+ import type { FetchRequest } from '../client';
5
+
6
+ /**
7
+ * Extends @see {ProcessOperators} for pre-processing requests.
8
+ */
9
+ export class HttpRequestHandler<T extends FetchRequest = FetchRequest> extends ProcessOperators<T> {
10
+ /**
11
+ * Set header that will apply on all requests done by consumer @see {HttpClient}
12
+ * @param key - name of header
13
+ * @param value - header value
14
+ */
15
+ setHeader(key: string, value: string): HttpRequestHandler<T> {
16
+ const operator = requestOperatorHeader<T>(key, value);
17
+ return this.set('header-' + key, operator) as HttpRequestHandler<T>;
18
+ }
19
+ }
20
+
21
+ export default HttpRequestHandler;
@@ -0,0 +1,5 @@
1
+ import { ProcessOperators } from './process-operators';
2
+
3
+ export class HttpResponseHandler<T = Response> extends ProcessOperators<T> {}
4
+
5
+ export default HttpResponseHandler;
@@ -0,0 +1,5 @@
1
+ export { HttpRequestHandler } from './http-request-handler';
2
+ export { HttpResponseHandler } from './http-response-handler';
3
+ export { ProcessOperators } from './process-operators';
4
+
5
+ export * from './types';