@equinor/fusion-framework-module-http 8.1.0 → 8.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +6 -3
- package/CHANGELOG.md +0 -1299
- package/docs/client-configuration.md +0 -175
- package/docs/observable-patterns.md +0 -103
- package/docs/selectors-and-handlers.md +0 -112
- package/docs/server-sent-events.md +0 -125
- package/docs/testing.md +0 -78
- package/src/configurator.ts +0 -249
- package/src/errors/ClientNotFoundException.ts +0 -9
- package/src/errors/HttpJsonResponseError.ts +0 -32
- package/src/errors/HttpResponseError.ts +0 -21
- package/src/errors/ServerSentEventResponseError.ts +0 -30
- package/src/errors/index.ts +0 -4
- package/src/index.ts +0 -15
- package/src/lib/client/client-msal.ts +0 -80
- package/src/lib/client/client.ts +0 -496
- package/src/lib/client/index.ts +0 -4
- package/src/lib/client/types.ts +0 -244
- package/src/lib/index.ts +0 -3
- package/src/lib/operators/HttpMiddlewareHandler.ts +0 -58
- package/src/lib/operators/HttpRequestHandler.ts +0 -29
- package/src/lib/operators/HttpResponseHandler.ts +0 -11
- package/src/lib/operators/ProcessOperators.ts +0 -113
- package/src/lib/operators/capitalize-request-method-operator.ts +0 -26
- package/src/lib/operators/fetch-request.schemas.ts +0 -104
- package/src/lib/operators/index.ts +0 -9
- package/src/lib/operators/request-operator-header.ts +0 -19
- package/src/lib/operators/request-validation-operator.ts +0 -51
- package/src/lib/operators/sse-map.operator.ts +0 -45
- package/src/lib/operators/types.ts +0 -174
- package/src/lib/selectors/blob-selector.ts +0 -42
- package/src/lib/selectors/create-sse-selector.ts +0 -279
- package/src/lib/selectors/index.ts +0 -11
- package/src/lib/selectors/json-selector.ts +0 -52
- package/src/mock/create-open-api-mock-middleware.ts +0 -40
- package/src/mock/create-router-middleware.ts +0 -158
- package/src/mock/index.ts +0 -26
- package/src/mock/resolve-open-api-mock-response.ts +0 -36
- package/src/module.ts +0 -149
- package/src/provider.ts +0 -225
- package/src/version.ts +0 -2
- package/tests/HttpClient.test.ts +0 -173
- package/tests/HttpMiddlewareHandler.test.ts +0 -58
- package/tests/mock/adapters.test.ts +0 -62
- package/tests/mock/router-middleware.test.ts +0 -135
- package/tests/operators.test.ts +0 -137
- package/tests/sse.selector.test.ts +0 -162
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -12
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import type { HttpMiddleware } from '../lib/operators/types';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* A route match handed to a {@link MockRouteHandler} once its pattern has matched a request.
|
|
5
|
-
*/
|
|
6
|
-
export interface MockRouteMatch {
|
|
7
|
-
/** Path parameters extracted from named segments in the route pattern, e.g. `:id` -> `params.id`. */
|
|
8
|
-
params: Record<string, string>;
|
|
9
|
-
/** The fully resolved request URL, for reading query parameters via `url.searchParams`. */
|
|
10
|
-
url: URL;
|
|
11
|
-
/** The request as a Fetch-standard `Request`, for reading headers or a JSON/text body. */
|
|
12
|
-
request: Request;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/** Builds the `Response` for one matched route. */
|
|
16
|
-
export type MockRouteHandler = (match: MockRouteMatch) => Response | Promise<Response>;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Registers route handlers for {@link createRouterMiddleware}, in the style of a minimal
|
|
20
|
-
* Express-like router — path templates (`:id`) and per-method registration, without pulling
|
|
21
|
-
* in a real routing library.
|
|
22
|
-
*/
|
|
23
|
-
export interface IMockRouterBuilder {
|
|
24
|
-
/** Registers `handler` for `GET` requests matching `path`. @see {@link IMockRouterBuilder.on} */
|
|
25
|
-
get(path: string, handler: MockRouteHandler): IMockRouterBuilder;
|
|
26
|
-
/** Registers `handler` for `POST` requests matching `path`. @see {@link IMockRouterBuilder.on} */
|
|
27
|
-
post(path: string, handler: MockRouteHandler): IMockRouterBuilder;
|
|
28
|
-
/** Registers `handler` for `PUT` requests matching `path`. @see {@link IMockRouterBuilder.on} */
|
|
29
|
-
put(path: string, handler: MockRouteHandler): IMockRouterBuilder;
|
|
30
|
-
/** Registers `handler` for `PATCH` requests matching `path`. @see {@link IMockRouterBuilder.on} */
|
|
31
|
-
patch(path: string, handler: MockRouteHandler): IMockRouterBuilder;
|
|
32
|
-
/** Registers `handler` for `DELETE` requests matching `path`. @see {@link IMockRouterBuilder.on} */
|
|
33
|
-
delete(path: string, handler: MockRouteHandler): IMockRouterBuilder;
|
|
34
|
-
/**
|
|
35
|
-
* Registers `handler` for a method and path template.
|
|
36
|
-
* @param method - The HTTP method to match, case-insensitively, or `undefined` to match any method.
|
|
37
|
-
* @param path - A path template relative to the router's base URI. A segment starting with
|
|
38
|
-
* `:` (e.g. `/contexts/:id`) captures that segment into `params`; every other segment must
|
|
39
|
-
* match literally. A trailing slash is always optional.
|
|
40
|
-
* @param handler - Builds the `Response` for a matching request.
|
|
41
|
-
*/
|
|
42
|
-
on(method: string | undefined, path: string, handler: MockRouteHandler): IMockRouterBuilder;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface RegisteredRoute {
|
|
46
|
-
method: string | undefined;
|
|
47
|
-
regexp: RegExp;
|
|
48
|
-
keys: string[];
|
|
49
|
-
handler: MockRouteHandler;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Compiles a path template into a matching `RegExp` plus the ordered list of named parameters
|
|
54
|
-
* it captures.
|
|
55
|
-
*
|
|
56
|
-
* @param path - A path template such as `/contexts/:id`.
|
|
57
|
-
* @returns The compiled pattern and the parameter names it captures, in segment order.
|
|
58
|
-
*/
|
|
59
|
-
function compilePath(path: string): { regexp: RegExp; keys: string[] } {
|
|
60
|
-
const keys: string[] = [];
|
|
61
|
-
const pattern = path
|
|
62
|
-
.split('/')
|
|
63
|
-
// each segment either captures a path parameter or must match literally
|
|
64
|
-
.map((segment) => {
|
|
65
|
-
// only `:name` segments become capture groups; everything else must match literally
|
|
66
|
-
if (!segment.startsWith(':')) {
|
|
67
|
-
return segment.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
68
|
-
}
|
|
69
|
-
keys.push(segment.slice(1));
|
|
70
|
-
return '([^/]+)';
|
|
71
|
-
})
|
|
72
|
-
.join('/');
|
|
73
|
-
return { regexp: new RegExp(`^${pattern}/?$`), keys };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Builds an {@link HttpMiddleware} that answers requests to one base URI with hand-registered
|
|
78
|
-
* route handlers, matched by method and a path template (`:id`-style segments) — a lightweight
|
|
79
|
-
* router for tests with more than a couple of routes to fake, without hand-rolling `RegExp`
|
|
80
|
-
* matching against `uri` in every middleware.
|
|
81
|
-
*
|
|
82
|
-
* @remarks
|
|
83
|
-
* A request outside `baseUri`, or matching no registered route, falls through to `next`, so
|
|
84
|
-
* this composes with other middleware — including the real network call, or another router
|
|
85
|
-
* for a different base URI — registered around it. Routes are tried in registration order;
|
|
86
|
-
* the first match wins.
|
|
87
|
-
*
|
|
88
|
-
* Deliberately not an MSW-compatible API — this stays inside `addMiddleware`'s own request
|
|
89
|
-
* pipeline rather than intercepting the network boundary, so it has none of MSW's response
|
|
90
|
-
* transformers, `onUnhandledRequest` diagnostics, or wildcard patterns.
|
|
91
|
-
*
|
|
92
|
-
* @param baseUri - The base URI this router answers for, e.g. `https://api.example.com`.
|
|
93
|
-
* @param build - Registers routes on the given {@link IMockRouterBuilder}.
|
|
94
|
-
* @returns A middleware for {@link IHttpClientConfigurator.addMiddleware}.
|
|
95
|
-
*
|
|
96
|
-
* @example Fake a handful of routes under one base URI
|
|
97
|
-
* ```typescript
|
|
98
|
-
* configurator.http.addMiddleware(
|
|
99
|
-
* createRouterMiddleware('https://context.example.com', (router) => {
|
|
100
|
-
* router.get('/contexts/:id/relations', () => Response.json([{ id: 'ctx-3' }]));
|
|
101
|
-
* router.get('/contexts', () => Response.json([{ id: 'ctx-2' }]));
|
|
102
|
-
* router.get('/contexts/:id', ({ params }) => Response.json({ id: params.id }));
|
|
103
|
-
* }),
|
|
104
|
-
* );
|
|
105
|
-
* ```
|
|
106
|
-
*/
|
|
107
|
-
export function createRouterMiddleware(
|
|
108
|
-
baseUri: string,
|
|
109
|
-
build: (router: IMockRouterBuilder) => void,
|
|
110
|
-
): HttpMiddleware {
|
|
111
|
-
const routes: RegisteredRoute[] = [];
|
|
112
|
-
const register = (
|
|
113
|
-
method: string | undefined,
|
|
114
|
-
path: string,
|
|
115
|
-
handler: MockRouteHandler,
|
|
116
|
-
): IMockRouterBuilder => {
|
|
117
|
-
const { regexp, keys } = compilePath(path);
|
|
118
|
-
routes.push({ method: method?.toUpperCase(), regexp, keys, handler });
|
|
119
|
-
return router;
|
|
120
|
-
};
|
|
121
|
-
const router: IMockRouterBuilder = {
|
|
122
|
-
get: (path, handler) => register('GET', path, handler),
|
|
123
|
-
post: (path, handler) => register('POST', path, handler),
|
|
124
|
-
put: (path, handler) => register('PUT', path, handler),
|
|
125
|
-
patch: (path, handler) => register('PATCH', path, handler),
|
|
126
|
-
delete: (path, handler) => register('DELETE', path, handler),
|
|
127
|
-
on: register,
|
|
128
|
-
};
|
|
129
|
-
build(router);
|
|
130
|
-
|
|
131
|
-
const base = new URL(baseUri);
|
|
132
|
-
const basePath = base.pathname.replace(/\/$/, '');
|
|
133
|
-
|
|
134
|
-
return async (uri, init, next) => {
|
|
135
|
-
const url = new URL(uri);
|
|
136
|
-
// requests to a different origin, or outside this router's base path, are none of its concern
|
|
137
|
-
if (url.origin !== base.origin || !url.pathname.startsWith(basePath)) return next(uri, init);
|
|
138
|
-
|
|
139
|
-
const pathname = url.pathname.slice(basePath.length) || '/';
|
|
140
|
-
const method = (init.method ?? 'GET').toUpperCase();
|
|
141
|
-
|
|
142
|
-
// first registered route whose method and pattern both match wins
|
|
143
|
-
for (const route of routes) {
|
|
144
|
-
// a route registered for a specific method never answers a request for another method
|
|
145
|
-
if (route.method && route.method !== method) continue;
|
|
146
|
-
const match = route.regexp.exec(pathname);
|
|
147
|
-
// pattern didn't match this pathname at all — try the next registered route
|
|
148
|
-
if (!match) continue;
|
|
149
|
-
// capture groups are positional, in the same order `compilePath` recorded their names
|
|
150
|
-
const params = Object.fromEntries(route.keys.map((key, i) => [key, match[i + 1]]));
|
|
151
|
-
return route.handler({ params, url, request: new Request(uri, init) });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return next(uri, init);
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export default createRouterMiddleware;
|
package/src/mock/index.ts
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Testing utilities for the HTTP module.
|
|
3
|
-
*
|
|
4
|
-
* @remarks
|
|
5
|
-
* Imported from `@equinor/fusion-framework-module-http/mock`, so testing
|
|
6
|
-
* utilities ship and version with the implementation they stand in for.
|
|
7
|
-
*
|
|
8
|
-
* There is no separate mock client or configurator here — register a
|
|
9
|
-
* short-circuiting `HttpMiddleware` through `configurator.http.addMiddleware(...)`
|
|
10
|
-
* on the real `HttpClientConfigurator` instead, so app config never has to
|
|
11
|
-
* branch on whether it's under test. See {@link createOpenApiMockMiddleware}
|
|
12
|
-
* for faking a whole OpenAPI document's operations that way.
|
|
13
|
-
*
|
|
14
|
-
* This entry point has no dependency on any test runner.
|
|
15
|
-
*
|
|
16
|
-
* @packageDocumentation
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
export { createOpenApiMockMiddleware } from './create-open-api-mock-middleware';
|
|
20
|
-
export type { OpenApiMockLike } from './resolve-open-api-mock-response';
|
|
21
|
-
export {
|
|
22
|
-
createRouterMiddleware,
|
|
23
|
-
type MockRouteHandler,
|
|
24
|
-
type MockRouteMatch,
|
|
25
|
-
type IMockRouterBuilder,
|
|
26
|
-
} from './create-router-middleware';
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The subset of `OpenApiMock` (from `@equinor/fusion-openapi-mock`'s
|
|
3
|
-
* `createOpenApiMock`) the adapters in this file need — duck-typed so this
|
|
4
|
-
* package has no dependency on that one; anything shaped like this works.
|
|
5
|
-
*/
|
|
6
|
-
export interface OpenApiMockLike {
|
|
7
|
-
resolve(request: {
|
|
8
|
-
method: string;
|
|
9
|
-
path: string;
|
|
10
|
-
query?: Record<string, string>;
|
|
11
|
-
}): Promise<{ status: number; mock: unknown } | undefined>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Resolves a method and URL against an `OpenApiMock`, building a `Response`
|
|
16
|
-
* from whatever it matches — shared by every adapter targeting a different
|
|
17
|
-
* middleware shape, so the method/path/query mapping and status/body wiring
|
|
18
|
-
* lives in one place.
|
|
19
|
-
*
|
|
20
|
-
* @param openApiMock - The mock to resolve against.
|
|
21
|
-
* @param method - The request's HTTP method.
|
|
22
|
-
* @param url - The request's fully resolved URL.
|
|
23
|
-
* @returns The faked `Response`, or `undefined` when no operation matches.
|
|
24
|
-
*/
|
|
25
|
-
export async function resolveOpenApiMockResponse(
|
|
26
|
-
openApiMock: OpenApiMockLike,
|
|
27
|
-
method: string,
|
|
28
|
-
url: URL,
|
|
29
|
-
): Promise<Response | undefined> {
|
|
30
|
-
const result = await openApiMock.resolve({
|
|
31
|
-
method,
|
|
32
|
-
path: url.pathname,
|
|
33
|
-
query: Object.fromEntries(url.searchParams),
|
|
34
|
-
});
|
|
35
|
-
return result && Response.json(result.mock, { status: result.status });
|
|
36
|
-
}
|
package/src/module.ts
DELETED
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { HttpClientMsal } from './lib/client';
|
|
3
|
-
import {
|
|
4
|
-
type IHttpClientConfigurator,
|
|
5
|
-
HttpClientConfigurator,
|
|
6
|
-
type HttpClientOptions,
|
|
7
|
-
} from './configurator';
|
|
8
|
-
import { type IHttpClientProvider, HttpClientProvider } from './provider';
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
Module,
|
|
12
|
-
ModuleConfigType,
|
|
13
|
-
IModuleConfigurator,
|
|
14
|
-
} from '@equinor/fusion-framework-module';
|
|
15
|
-
|
|
16
|
-
import type { MsalModule } from '@equinor/fusion-framework-module-msal';
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Defines the type for the HTTP module, which includes:
|
|
20
|
-
* - The module name: 'http'
|
|
21
|
-
* - The type of the HTTP client provider, which is `IHttpClientProvider`
|
|
22
|
-
* - The type of the HTTP client configurator, which is `IHttpClientConfigurator`
|
|
23
|
-
*/
|
|
24
|
-
export type HttpModule = Module<'http', IHttpClientProvider, IHttpClientConfigurator>;
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Defines the type for the HTTP module with MSAL authentication.
|
|
28
|
-
*
|
|
29
|
-
* This type represents the module configuration for the HTTP module, which includes:
|
|
30
|
-
* - The module name: 'http'
|
|
31
|
-
* - The type of the HTTP client provider, which is `IHttpClientProvider<HttpClientMsal>`
|
|
32
|
-
* - The type of the HTTP client configurator, which is `IHttpClientConfigurator<HttpClientMsal>`
|
|
33
|
-
* - The list of required modules, which includes the `MsalModule`
|
|
34
|
-
*/
|
|
35
|
-
export type HttpMsalModule = Module<
|
|
36
|
-
'http',
|
|
37
|
-
IHttpClientProvider<HttpClientMsal>,
|
|
38
|
-
IHttpClientConfigurator<HttpClientMsal>,
|
|
39
|
-
[MsalModule]
|
|
40
|
-
>;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Default HTTP module definition for Fusion Framework applications.
|
|
44
|
-
*
|
|
45
|
-
* The module uses `HttpClientMsal` as the default client implementation and,
|
|
46
|
-
* when the auth module is available, installs a request handler that can acquire
|
|
47
|
-
* bearer tokens for scoped requests.
|
|
48
|
-
*/
|
|
49
|
-
export const module: HttpMsalModule = {
|
|
50
|
-
name: 'http',
|
|
51
|
-
/**
|
|
52
|
-
* Configures the HTTP module with MSAL authentication.
|
|
53
|
-
*
|
|
54
|
-
* This function creates a new `HttpClientConfigurator` instance using the `HttpClientMsal` class.
|
|
55
|
-
* The `HttpClientConfigurator` is responsible for configuring the HTTP client with the necessary options,
|
|
56
|
-
* such as the base URL, request headers, and other settings.
|
|
57
|
-
*
|
|
58
|
-
* @returns A new `HttpClientConfigurator` instance configured for MSAL authentication.
|
|
59
|
-
*/
|
|
60
|
-
configure: () => new HttpClientConfigurator(HttpClientMsal),
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Initializes the HTTP client provider with MSAL authentication.
|
|
64
|
-
*
|
|
65
|
-
* This function is responsible for setting up the default HTTP request handler
|
|
66
|
-
* to acquire an access token from MSAL and attach it to the request headers
|
|
67
|
-
* when the request includes scopes.
|
|
68
|
-
*
|
|
69
|
-
* @param config - The module configuration.
|
|
70
|
-
* @param hasModule - A function to check if a module is available.
|
|
71
|
-
* @param requireInstance - A function to get an instance of a module.
|
|
72
|
-
* @returns A promise that resolves to the HTTP client provider.
|
|
73
|
-
*/
|
|
74
|
-
initialize: async ({
|
|
75
|
-
config,
|
|
76
|
-
hasModule,
|
|
77
|
-
requireInstance,
|
|
78
|
-
}): Promise<HttpClientProvider<HttpClientMsal>> => {
|
|
79
|
-
const httpProvider = new HttpClientProvider(config);
|
|
80
|
-
// wire up an MSAL bearer-token handler only when the auth module is registered
|
|
81
|
-
if (hasModule('auth')) {
|
|
82
|
-
const authProvider = await requireInstance('auth');
|
|
83
|
-
httpProvider.defaultHttpRequestHandler.set('MSAL', async (request) => {
|
|
84
|
-
const { scopes = [] } = request;
|
|
85
|
-
// only attempt to acquire a token when the request actually declares scopes
|
|
86
|
-
if (scopes.length) {
|
|
87
|
-
/** TODO(#5143): should be try catch, check caller for handling */
|
|
88
|
-
const accessToken = await authProvider.acquireAccessToken({
|
|
89
|
-
request: { scopes },
|
|
90
|
-
});
|
|
91
|
-
// without a token there's nothing to attach, fall through to the default request
|
|
92
|
-
if (accessToken) {
|
|
93
|
-
const headers = new Headers(request.headers);
|
|
94
|
-
headers.set('Authorization', `Bearer ${accessToken}`);
|
|
95
|
-
return { ...request, headers };
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
return httpProvider;
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Creates a module configurator for the HTTP module.
|
|
106
|
-
*
|
|
107
|
-
* Use this when you need lower-level access to the module configuration callback,
|
|
108
|
-
* for example when registering several named clients in one setup step.
|
|
109
|
-
*
|
|
110
|
-
* @param configure - The callback that receives the HTTP module configuration.
|
|
111
|
-
* @returns A module configurator for the HTTP module.
|
|
112
|
-
*/
|
|
113
|
-
export const configureHttp = <TRef = unknown>(
|
|
114
|
-
configure: (config: ModuleConfigType<HttpMsalModule>, ref?: TRef) => void,
|
|
115
|
-
): IModuleConfigurator<HttpMsalModule, TRef> => ({
|
|
116
|
-
module,
|
|
117
|
-
configure,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Creates a module configurator that registers one named HTTP client.
|
|
122
|
-
*
|
|
123
|
-
* This is the convenience API for the common case where a setup step only needs to
|
|
124
|
-
* register one client with `baseUri`, `defaultScopes`, handlers, or a custom constructor.
|
|
125
|
-
*
|
|
126
|
-
* @param name - The client key used later with `createClient(name)`.
|
|
127
|
-
* @param args - The named client configuration.
|
|
128
|
-
* @returns A module configurator that registers the named client.
|
|
129
|
-
*/
|
|
130
|
-
export const configureHttpClient = <TRef = unknown>(
|
|
131
|
-
name: string,
|
|
132
|
-
args: HttpClientOptions<HttpClientMsal>,
|
|
133
|
-
): IModuleConfigurator<HttpMsalModule, TRef> => ({
|
|
134
|
-
module,
|
|
135
|
-
configure: (config: ModuleConfigType<HttpMsalModule>) => {
|
|
136
|
-
config.configureClient(name, args);
|
|
137
|
-
},
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Declares a module named '@equinor/fusion-framework-module' that contains an interface named 'Modules' with a property 'http' of type 'HttpMsalModule'.
|
|
142
|
-
*/
|
|
143
|
-
declare module '@equinor/fusion-framework-module' {
|
|
144
|
-
interface Modules {
|
|
145
|
-
http: HttpMsalModule;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
export default module;
|
package/src/provider.ts
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import type { HttpClient } from './lib/client';
|
|
2
|
-
import type {
|
|
3
|
-
HttpClientOptions,
|
|
4
|
-
HttpClientRequestInitType,
|
|
5
|
-
IHttpClientConfigurator,
|
|
6
|
-
} from './configurator';
|
|
7
|
-
|
|
8
|
-
import type { IHttpMiddlewareHandler, IHttpRequestHandler } from './lib/operators';
|
|
9
|
-
import type { IHttpClient } from './lib/client';
|
|
10
|
-
import { BaseModuleProvider } from '@equinor/fusion-framework-module/provider';
|
|
11
|
-
import { version } from './version';
|
|
12
|
-
import { ClientNotFoundException } from './errors/index.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Creates fresh HTTP client instances from named or ad-hoc configuration.
|
|
16
|
-
*
|
|
17
|
-
* A provider can create clients from registered names, inline `HttpClientOptions`,
|
|
18
|
-
* or absolute URLs treated as ad-hoc `baseUri` values.
|
|
19
|
-
*/
|
|
20
|
-
export interface IHttpClientProvider<TClient extends IHttpClient = IHttpClient> {
|
|
21
|
-
/**
|
|
22
|
-
* The default HTTP request handler used by the HttpClientProvider.
|
|
23
|
-
* This handler is responsible for executing HTTP requests using the configured HttpClient.
|
|
24
|
-
*/
|
|
25
|
-
readonly defaultHttpRequestHandler: IHttpRequestHandler<HttpClientRequestInitType<TClient>>;
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* The default middleware chain used by the HttpClientProvider, wrapping the network call
|
|
29
|
-
* for every client it creates unless a client overrides it with its own `middlewareHandler`.
|
|
30
|
-
*/
|
|
31
|
-
readonly defaultHttpMiddlewareHandler: IHttpMiddlewareHandler;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Checks if a client is configured with the given key.
|
|
35
|
-
* @param key - The key of the client to check.
|
|
36
|
-
* @returns `true` if a client is configured with the given key, `false` otherwise.
|
|
37
|
-
*/
|
|
38
|
-
hasClient(key: string): boolean;
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Creates a new HTTP client instance with the specified key.
|
|
42
|
-
* @param key - The key of the HTTP client to create.
|
|
43
|
-
* @returns The created HTTP client instance.
|
|
44
|
-
*/
|
|
45
|
-
createClient(key: string): TClient;
|
|
46
|
-
createClient(key: HttpClientOptions<TClient>): TClient;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Creates a client instance and casts it to a custom HTTP client type.
|
|
50
|
-
*
|
|
51
|
-
* This is most useful when the named client configuration uses a custom `ctor`
|
|
52
|
-
* that extends `HttpClient` with domain-specific methods.
|
|
53
|
-
* @example
|
|
54
|
-
* ```ts
|
|
55
|
-
* config.http.configureClient('foobar', {
|
|
56
|
-
* ctor: MyClient,
|
|
57
|
-
* baseUri: 'https://foobar.com',
|
|
58
|
-
* });
|
|
59
|
-
* ```
|
|
60
|
-
*/
|
|
61
|
-
createCustomClient<T extends HttpClient>(key: string): T;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/** URL protocols accepted as valid ad-hoc base URIs. */
|
|
65
|
-
const SUPPORTED_PROTOCOLS = ['http:', 'https:', 'ws:', 'wss:'] as const;
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Checks if a given string is a valid absolute URL with a supported protocol.
|
|
69
|
-
* @param url - The string to check.
|
|
70
|
-
* @returns `true` when the string uses one of {@link SUPPORTED_PROTOCOLS}.
|
|
71
|
-
*/
|
|
72
|
-
const isURL = (url: string): boolean => {
|
|
73
|
-
try {
|
|
74
|
-
const parsed = new URL(url);
|
|
75
|
-
return (SUPPORTED_PROTOCOLS as readonly string[]).includes(parsed.protocol);
|
|
76
|
-
} catch {
|
|
77
|
-
return false;
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Heuristic check for strings that look like a bare hostname or URL without a
|
|
83
|
-
* protocol prefix (e.g. `api.example.com` or `api.example.com/v1`).
|
|
84
|
-
*
|
|
85
|
-
* Used to emit a deprecation warning when callers rely on the old (pre-patch)
|
|
86
|
-
* behaviour that accepted protocol-less URLs.
|
|
87
|
-
*/
|
|
88
|
-
const looksLikeURL = (value: string): boolean =>
|
|
89
|
-
!value.includes(' ') && /^[a-z\d]([a-z\d-]*\.)+[a-z]{2,}/i.test(value);
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* The `HttpClientProvider` class is responsible for managing HTTP client instances and their configuration.
|
|
93
|
-
* It provides methods to check if a client is configured, create new client instances, and create custom client instances.
|
|
94
|
-
*/
|
|
95
|
-
export class HttpClientProvider<TClient extends IHttpClient = IHttpClient>
|
|
96
|
-
extends BaseModuleProvider<IHttpClientConfigurator<TClient>>
|
|
97
|
-
implements IHttpClientProvider<TClient>
|
|
98
|
-
{
|
|
99
|
-
/**
|
|
100
|
-
* Gets the default HTTP request handler for the HTTP client provider.
|
|
101
|
-
* @returns The default HTTP request handler.
|
|
102
|
-
*/
|
|
103
|
-
get defaultHttpRequestHandler(): IHttpRequestHandler<HttpClientRequestInitType<TClient>> {
|
|
104
|
-
return this.config.defaultHttpRequestHandler;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Gets the default middleware chain for the HTTP client provider.
|
|
109
|
-
* @returns The default middleware chain.
|
|
110
|
-
*/
|
|
111
|
-
get defaultHttpMiddlewareHandler(): IHttpMiddlewareHandler {
|
|
112
|
-
return this.config.defaultHttpMiddlewareHandler;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Creates a new `HttpClientProvider`.
|
|
117
|
-
* @param config - The configurator providing client definitions and defaults.
|
|
118
|
-
*/
|
|
119
|
-
constructor(protected config: IHttpClientConfigurator<TClient>) {
|
|
120
|
-
super({
|
|
121
|
-
version,
|
|
122
|
-
config,
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Checks if a client with the given key is configured in the `HttpClientProvider`.
|
|
128
|
-
* @param key - The key of the HTTP client to check.
|
|
129
|
-
* @returns `true` if a client with the given key is configured, `false` otherwise.
|
|
130
|
-
*/
|
|
131
|
-
public hasClient(key: string): boolean {
|
|
132
|
-
return Object.keys(this.config.clients).includes(key);
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Creates a fresh HTTP client instance from a named or ad-hoc configuration.
|
|
137
|
-
*
|
|
138
|
-
* @param keyOrConfig - The key or configuration object for the HTTP client.
|
|
139
|
-
* @returns The created HTTP client instance.
|
|
140
|
-
*
|
|
141
|
-
* @remarks
|
|
142
|
-
* This method resolves the configuration for the HTTP client based on the provided `keyOrConfig` parameter.
|
|
143
|
-
* If a string is provided, it is treated as the key for a pre-configured client in the `HttpClientProvider`.
|
|
144
|
-
* If an `HttpClientOptions` object is provided, it is used as the configuration for the new client instance.
|
|
145
|
-
*
|
|
146
|
-
* The method applies `baseUri`, `defaultScopes`, `requestHandler`, `responseHandler`,
|
|
147
|
-
* a custom `ctor` when configured, and finally runs `onCreate` for the newly created instance.
|
|
148
|
-
*
|
|
149
|
-
* The created HTTP client instance is returned.
|
|
150
|
-
*/
|
|
151
|
-
public createClient(keyOrConfig: string | HttpClientOptions<TClient>): TClient {
|
|
152
|
-
const config = this._resolveConfig(keyOrConfig);
|
|
153
|
-
const {
|
|
154
|
-
baseUri,
|
|
155
|
-
defaultScopes = [],
|
|
156
|
-
onCreate,
|
|
157
|
-
ctor = this.config.defaultHttpClientCtor,
|
|
158
|
-
requestHandler = this.defaultHttpRequestHandler,
|
|
159
|
-
responseHandler,
|
|
160
|
-
middlewareHandler = this.defaultHttpMiddlewareHandler,
|
|
161
|
-
} = config as HttpClientOptions<TClient>;
|
|
162
|
-
const options = { requestHandler, responseHandler, middlewareHandler };
|
|
163
|
-
const instance = new ctor(baseUri || '', options) as TClient;
|
|
164
|
-
// attach the resolved default scopes onto the instance without overwriting other own properties
|
|
165
|
-
Object.assign(instance, { defaultScopes });
|
|
166
|
-
onCreate?.(instance as TClient);
|
|
167
|
-
return instance as TClient;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Creates a client instance and returns it as the requested custom client type.
|
|
172
|
-
*
|
|
173
|
-
* @template T - The custom `HttpClient` implementation type to cast the created instance to.
|
|
174
|
-
* @param key - The key of the pre-configured HTTP client to create.
|
|
175
|
-
* @returns The created HTTP client instance, cast to the specified type `T`.
|
|
176
|
-
*
|
|
177
|
-
* @remarks
|
|
178
|
-
* This method delegates to the `createClient` method, but casts the returned
|
|
179
|
-
* instance to the specified type `T`. This can be useful when you need to
|
|
180
|
-
* work with a specific HTTP client implementation, but the `HttpClientProvider`
|
|
181
|
-
* is configured to use a different implementation.
|
|
182
|
-
*/
|
|
183
|
-
public createCustomClient<T extends HttpClient>(key: string): T {
|
|
184
|
-
// `createClient` always returns the provider's configured `HttpClient` implementation —
|
|
185
|
-
// cast through `unknown` to hand back the caller-requested implementation type `T`.
|
|
186
|
-
return this.createClient(key) as unknown as T;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Resolves the configuration for an HTTP client based on the provided `keyOrConfig` parameter.
|
|
191
|
-
*
|
|
192
|
-
* If a string is provided, it is treated as either a pre-configured client key or,
|
|
193
|
-
* when it is an absolute `http:` or `https:` URL, as an ad-hoc `baseUri`.
|
|
194
|
-
* If an `HttpClientOptions` object is provided, it is used directly as the configuration for the new client instance.
|
|
195
|
-
*
|
|
196
|
-
* @param keyOrConfig - The key or configuration object for the HTTP client.
|
|
197
|
-
* @returns The resolved HTTP client configuration.
|
|
198
|
-
* @throws {ClientNotFoundException} When `keyOrConfig` is a string that is neither a registered
|
|
199
|
-
* client key nor a URL-like value.
|
|
200
|
-
*/
|
|
201
|
-
protected _resolveConfig(
|
|
202
|
-
keyOrConfig: string | HttpClientOptions<TClient>,
|
|
203
|
-
): HttpClientOptions<TClient> {
|
|
204
|
-
// a string may reference a registered client key or an ad-hoc URL; anything else is used as-is
|
|
205
|
-
if (typeof keyOrConfig === 'string') {
|
|
206
|
-
const config = this.config.clients[keyOrConfig];
|
|
207
|
-
// an absolute http(s) URL can be used directly as an ad-hoc baseUri
|
|
208
|
-
if (!config && isURL(keyOrConfig)) {
|
|
209
|
-
return { baseUri: keyOrConfig };
|
|
210
|
-
} else if (!config && looksLikeURL(keyOrConfig)) {
|
|
211
|
-
// recover from a missing protocol instead of failing outright, but warn so it can be fixed
|
|
212
|
-
console.warn(
|
|
213
|
-
`[HttpClientProvider] "${keyOrConfig}" looks like a URL but is missing the http:// or https:// protocol. ` +
|
|
214
|
-
`Treating it as "https://${keyOrConfig}". ` +
|
|
215
|
-
`Pass a fully-qualified URL to silence this warning.`,
|
|
216
|
-
);
|
|
217
|
-
return { baseUri: `https://${keyOrConfig}` };
|
|
218
|
-
} else if (!config) {
|
|
219
|
-
throw new ClientNotFoundException(`No registered http client for key [${keyOrConfig}]`);
|
|
220
|
-
}
|
|
221
|
-
return config;
|
|
222
|
-
}
|
|
223
|
-
return keyOrConfig as HttpClientOptions<TClient>;
|
|
224
|
-
}
|
|
225
|
-
}
|
package/src/version.ts
DELETED