@equinor/fusion-framework-module-http 8.1.0-next.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.
Files changed (53) hide show
  1. package/dist/esm/version.js +1 -1
  2. package/dist/esm/version.js.map +1 -1
  3. package/dist/tsconfig.tsbuildinfo +1 -1
  4. package/dist/types/version.d.ts +1 -1
  5. package/package.json +6 -3
  6. package/CHANGELOG.md +0 -1321
  7. package/docs/client-configuration.md +0 -175
  8. package/docs/observable-patterns.md +0 -103
  9. package/docs/selectors-and-handlers.md +0 -112
  10. package/docs/server-sent-events.md +0 -125
  11. package/docs/testing.md +0 -78
  12. package/src/configurator.ts +0 -249
  13. package/src/errors/ClientNotFoundException.ts +0 -9
  14. package/src/errors/HttpJsonResponseError.ts +0 -32
  15. package/src/errors/HttpResponseError.ts +0 -21
  16. package/src/errors/ServerSentEventResponseError.ts +0 -30
  17. package/src/errors/index.ts +0 -4
  18. package/src/index.ts +0 -15
  19. package/src/lib/client/client-msal.ts +0 -80
  20. package/src/lib/client/client.ts +0 -496
  21. package/src/lib/client/index.ts +0 -4
  22. package/src/lib/client/types.ts +0 -244
  23. package/src/lib/index.ts +0 -3
  24. package/src/lib/operators/HttpMiddlewareHandler.ts +0 -58
  25. package/src/lib/operators/HttpRequestHandler.ts +0 -29
  26. package/src/lib/operators/HttpResponseHandler.ts +0 -11
  27. package/src/lib/operators/ProcessOperators.ts +0 -113
  28. package/src/lib/operators/capitalize-request-method-operator.ts +0 -26
  29. package/src/lib/operators/fetch-request.schemas.ts +0 -104
  30. package/src/lib/operators/index.ts +0 -9
  31. package/src/lib/operators/request-operator-header.ts +0 -19
  32. package/src/lib/operators/request-validation-operator.ts +0 -51
  33. package/src/lib/operators/sse-map.operator.ts +0 -45
  34. package/src/lib/operators/types.ts +0 -174
  35. package/src/lib/selectors/blob-selector.ts +0 -42
  36. package/src/lib/selectors/create-sse-selector.ts +0 -279
  37. package/src/lib/selectors/index.ts +0 -11
  38. package/src/lib/selectors/json-selector.ts +0 -52
  39. package/src/mock/create-open-api-mock-middleware.ts +0 -40
  40. package/src/mock/create-router-middleware.ts +0 -158
  41. package/src/mock/index.ts +0 -26
  42. package/src/mock/resolve-open-api-mock-response.ts +0 -36
  43. package/src/module.ts +0 -149
  44. package/src/provider.ts +0 -225
  45. package/src/version.ts +0 -2
  46. package/tests/HttpClient.test.ts +0 -173
  47. package/tests/HttpMiddlewareHandler.test.ts +0 -58
  48. package/tests/mock/adapters.test.ts +0 -62
  49. package/tests/mock/router-middleware.test.ts +0 -135
  50. package/tests/operators.test.ts +0 -137
  51. package/tests/sse.selector.test.ts +0 -162
  52. package/tsconfig.json +0 -18
  53. package/vitest.config.ts +0 -12
@@ -1,45 +0,0 @@
1
- import type { OperatorFunction } from 'rxjs';
2
- import { switchMap } from 'rxjs/operators';
3
- import {
4
- createSseSelector,
5
- type ServerSentEvent,
6
- type SseSelectorOptions,
7
- } from '../selectors/create-sse-selector';
8
-
9
- /**
10
- * An operator function for handling Server-Sent Events (SSE) in an RxJS pipeline.
11
- *
12
- * @template R - The type of the parsed data from the SSE.
13
- * @template T - The type of the input `Response` object, defaults to `Response`.
14
- *
15
- * @param options - Configuration options for the SSE selector.
16
- *
17
- * @returns An `OperatorFunction` that transforms a stream of `Response` objects
18
- * into a stream of `ServerSentEvent` objects containing parsed data of type `R`.
19
- *
20
- * @example
21
- * ```typescript
22
- * import { sseMap } from '@equinor/fusion-framework-module-http/operators';
23
- * import { fromFetch } from 'rxjs/fetch';
24
- *
25
- * const response$ = fromFetch('https://example.com/sse', {
26
- * method: 'GET',
27
- * headers: {
28
- * 'Accept': 'text/event-stream',
29
- * },
30
- * }).pipe(
31
- * sseMap( { /* SSE selector options *\/ })
32
- * ).subscribe((event) => {
33
- * console.log(event.data); // Process the parsed SSE data
34
- * });
35
- * ```
36
- */
37
- export const sseMap =
38
- <R = unknown, T extends Response = Response>(
39
- options?: SseSelectorOptions<R>,
40
- ): OperatorFunction<T, ServerSentEvent<R>> =>
41
- (source) =>
42
- // parse each raw Response into a stream of typed server-sent events
43
- source.pipe(switchMap(createSseSelector<R, T>(options)));
44
-
45
- export default sseMap;
@@ -1,174 +0,0 @@
1
- import type { Observable, ObservableInput } from 'rxjs';
2
- import type { FetchRequest } from '../client';
3
-
4
- /**
5
- * A process operator that takes a request of type `T` and returns a transformed request of type `R`, or `void`, or a Promise that resolves to `R` or `void`.
6
- *
7
- * Process operators are used to transform or modify requests in a sequential pipeline before they are processed by an `IHttpRequestHandler`.
8
- *
9
- * @template T The type of the input request.
10
- * @template R The type of the output request. Defaults to `T` if not specified.
11
- * @param request The input request to be processed.
12
- * @returns The transformed request, `void`, or a Promise that resolves to the transformed request or `void`.
13
- */
14
- // biome-ignore lint/suspicious/noConfusingVoidType: `void` here relies on TypeScript's special-cased "void-returning callback accepts any return value" behavior for process operators \u2014 `undefined` would break assignability of operator functions that return a transformed request
15
- export type ProcessOperator<T, R = T> = (request: T) => R | void | Promise<R | void>;
16
-
17
- /**
18
- * Represents a collection of process operators.
19
- * @template T The type of the request being processed.
20
- */
21
- export interface IProcessOperators<T> {
22
- /**
23
- * Gets the operators registered in the collection.
24
- */
25
- get operators(): Record<string, ProcessOperator<T>>;
26
-
27
- /**
28
- * Adds a new operator to the collection.
29
- * @param key The key to identify the operator.
30
- * @param operator The process operator to add.
31
- * @returns The updated collection of process operators.
32
- * @throws An error if the operator is already defined.
33
- */
34
- add(key: string, operator: ProcessOperator<T>): IProcessOperators<T>;
35
-
36
- /**
37
- * Adds or sets a process operator in the collection.
38
- * @param key The key to identify the operator.
39
- * @param operator The process operator to add or set.
40
- * @returns The updated collection of process operators.
41
- */
42
- set(key: string, operator: ProcessOperator<T>): IProcessOperators<T>;
43
-
44
- /**
45
- * Removes a process operator from the collection.
46
- * @param key The key of the operator to remove.
47
- * @returns The updated collection of process operators.
48
- */
49
- remove(key: string): IProcessOperators<T>;
50
-
51
- /**
52
- * Gets a process operator from the collection.
53
- * @param key The key of the operator to retrieve.
54
- * @returns The process operator associated with the key, or undefined if the key is invalid.
55
- */
56
- get(key: string): ProcessOperator<T>;
57
-
58
- /**
59
- * Processes the registered process operators.
60
- * @param request The request to process.
61
- * @returns An observable that emits the processed request.
62
- */
63
- process(request: T): Observable<T>;
64
- }
65
-
66
- /**
67
- * Represents an HTTP request handler that extends the `IProcessOperators` interface.
68
- * This interface provides methods to manage and process HTTP request operators.
69
- *
70
- * @template T - The type of the request being processed. Defaults to `FetchRequest`.
71
- */
72
- export interface IHttpRequestHandler<T extends FetchRequest = FetchRequest>
73
- extends IProcessOperators<T> {
74
- /**
75
- * Set header that will apply on all requests done by consumer @see {HttpClient}
76
- * @param key - name of header
77
- * @param value - header value
78
- */
79
- setHeader(key: string, value: string): IHttpRequestHandler<T>;
80
- }
81
-
82
- /**
83
- * Represents an HTTP response handler that extends the `IProcessOperators` interface.
84
- * This interface provides methods to manage and process HTTP response operators.
85
- *
86
- * @template T - The type of the response being processed. Defaults to `Response`.
87
- */
88
- export interface IHttpResponseHandler<T = Response> extends IProcessOperators<T> {}
89
-
90
- /**
91
- * Continues an HTTP request by resolving the given (already-processed) request into a response.
92
- *
93
- * @remarks
94
- * The terminal `next` passed to the outermost {@link HttpMiddleware} ultimately resolves to
95
- * `HttpClient._performFetch` — the same overridable seam the mock system replaces — so
96
- * middleware wraps around either the real network call or a mocked one transparently.
97
- *
98
- * @param uri - The fully resolved URL for the request.
99
- * @param init - The prepared `fetch` request options.
100
- * @returns The resulting `Response`, or an observable input of it.
101
- */
102
- export type HttpMiddlewareNext = (
103
- uri: string,
104
- init: RequestInit,
105
- ) => Response | ObservableInput<Response>;
106
-
107
- /**
108
- * Continues to the next registered {@link HttpMiddleware}, or the network call itself,
109
- * always resolving to a `Response` regardless of how that next step actually produced it —
110
- * a short-circuited `Response`, a `Promise`, or an `Observable`.
111
- *
112
- * @param uri - The fully resolved URL for the request.
113
- * @param init - The prepared `fetch` request options.
114
- * @returns A promise of the resulting `Response`.
115
- */
116
- export type HttpMiddlewareContinuation = (uri: string, init: RequestInit) => Promise<Response>;
117
-
118
- /**
119
- * Wraps request execution to add cross-cutting behavior — retries, caching, telemetry,
120
- * circuit breaking — around the network call itself, rather than transforming the
121
- * request or response payload.
122
- *
123
- * @remarks
124
- * Unlike {@link ProcessOperator}, which transforms a value in a linear pipeline, a middleware
125
- * controls whether and how many times `next` runs: it can short-circuit by never calling
126
- * `next`, retry by calling it more than once, or recover from a rejection it throws.
127
- * Registered middleware compose in an "onion" — the first one registered is outermost, so it
128
- * sees the request first and the response last.
129
- *
130
- * @param uri - The fully resolved URL for the request.
131
- * @param init - The prepared `fetch` request options.
132
- * @param next - Continues to the next registered middleware, or the network call itself.
133
- * @returns The resulting `Response`, or an observable input of it.
134
- *
135
- * @example Retry once on a failed response
136
- * ```typescript
137
- * const retryOnce: HttpMiddleware = async (uri, init, next) => {
138
- * const response = await next(uri, init);
139
- * return response.ok ? response : next(uri, init);
140
- * };
141
- * ```
142
- */
143
- export type HttpMiddleware = (
144
- uri: string,
145
- init: RequestInit,
146
- next: HttpMiddlewareContinuation,
147
- ) => Response | ObservableInput<Response>;
148
-
149
- /**
150
- * Registers and composes {@link HttpMiddleware} into a single execution pipeline wrapping
151
- * the network call.
152
- */
153
- export interface IHttpMiddlewareHandler {
154
- /**
155
- * Gets the registered middleware, in registration order.
156
- */
157
- get middleware(): readonly HttpMiddleware[];
158
-
159
- /**
160
- * Registers a middleware, wrapping every middleware registered before it.
161
- * @param middleware - The middleware to register.
162
- * @returns The updated handler, for chaining.
163
- */
164
- use(middleware: HttpMiddleware): IHttpMiddlewareHandler;
165
-
166
- /**
167
- * Runs the registered middleware chain around a request, ending with `terminal`.
168
- * @param uri - The fully resolved URL for the request.
169
- * @param init - The prepared `fetch` request options.
170
- * @param terminal - The innermost step the chain wraps, called when every middleware defers to `next`.
171
- * @returns An observable of the resulting `Response`.
172
- */
173
- process(uri: string, init: RequestInit, terminal: HttpMiddlewareNext): Observable<Response>;
174
- }
@@ -1,42 +0,0 @@
1
- import type { ResponseSelector, BlobResult } from '../client/types';
2
-
3
- /**
4
- * Extracts a blob and filename from a successful HTTP response.
5
- *
6
- * @param response - The HTTP response to extract the blob and filename from.
7
- * @returns A promise that resolves to an object containing the extracted blob and filename.
8
- * @throws {Error} If the response is not successful or if there is an error parsing the response.
9
- */
10
- export const blobSelector: ResponseSelector = async <TResponse extends Response = Response>(
11
- response: TResponse,
12
- ): Promise<BlobResult> => {
13
- // treat any non-2xx response as a failure rather than attempting to read its body
14
- if (!response.ok) {
15
- throw new Error('network response was not OK');
16
- }
17
-
18
- // Status code 204 indicates no content, so throw an error
19
- if (response.status === 204) {
20
- throw new Error('no content');
21
- }
22
-
23
- // Extract the filename from the 'content-disposition' header
24
- // locate the segment carrying the filename directive; other directives (e.g. inline/attachment) are ignored
25
- const filename = response.headers
26
- .get('content-disposition')
27
- ?.split(';')
28
- .find((n) => n.includes('filename='))
29
- ?.replace('filename=', '')
30
- ?.trim();
31
-
32
- try {
33
- // Convert the response to a Blob and return the filename and Blob
34
- const blob = await response.blob();
35
- return { filename, blob };
36
- } catch (_err) {
37
- // Throw an error if there's a problem parsing the response
38
- throw Error('failed to parse response');
39
- }
40
- };
41
-
42
- export default blobSelector;
@@ -1,279 +0,0 @@
1
- import { EMPTY, from, fromEvent, type Observable } from 'rxjs';
2
- import { finalize, takeUntil } from 'rxjs/operators';
3
-
4
- import type { ResponseSelector } from '../client/types.js';
5
- import { ServerSentEventResponseError } from '../../errors/index.js';
6
-
7
- /**
8
- * A type representing a function that parses a string into a specific data type.
9
- *
10
- * @typeParam TData - The type of the parsed data. Defaults to `unknown` if not specified.
11
- * @param data - The string input to be parsed.
12
- * @returns The parsed data of type `TData`.
13
- */
14
- export type DataParser<TData = unknown> = (data: string) => TData;
15
-
16
- const defaultDataParser: DataParser = (data: string) => {
17
- try {
18
- return JSON.parse(data);
19
- } catch {
20
- return data;
21
- }
22
- };
23
-
24
- /**
25
- * Represents a Server-Sent Event (SSE) with optional fields.
26
- */
27
- export type ServerSentEvent<TData = unknown> = {
28
- id?: string;
29
- event?: string;
30
- data?: TData;
31
- retry?: string;
32
- };
33
-
34
- /**
35
- * Parses a string containing Server-Sent Events (SSE) data into individual event objects.
36
- *
37
- * @template TData - The type of the parsed `data` field in the event.
38
- * @param text - The raw SSE data as a string, where events are separated by double newlines.
39
- * @param options - Optional configuration for parsing the events.
40
- * @param options.dataParser - A custom parser function for the `data` field. Defaults to JSON parsing,
41
- * falling back to returning the raw string if parsing fails.
42
- *
43
- * @returns A generator that yields `ServerSentEvent` objects parsed from the input string.
44
- *
45
- * @remarks
46
- * - Empty lines and events with no fields are ignored.
47
- * - If the `data` field cannot be parsed as JSON, it is returned as a plain string.
48
- * - Fields other than `data` are stored as strings in the resulting `ServerSentEvent` object.
49
- *
50
- * @example
51
- * ```typescript
52
- * const sseData = `
53
- * id: 1
54
- * event: message
55
- * data: {"key":"value"}
56
- *
57
- * id: 2
58
- * event: update
59
- * data: plain text
60
- * `;
61
- *
62
- * for (const event of parseEvents(sseData)) {
63
- * console.log(event);
64
- * }
65
- * // Output:
66
- * // { id: "1", event: "message", data: { key: "value" } }
67
- * // { id: "2", event: "update", data: "plain text" }
68
- * ```
69
- */
70
- function* parseEvents<TData = unknown>(
71
- text: string,
72
- options?: { dataParser?: DataParser<TData> },
73
- ): Generator<ServerSentEvent<TData>> {
74
- // Split the input string into individual event strings using double newline as separator
75
- const eventStrings = text.split('\n\n');
76
-
77
- const dataParser = options?.dataParser || (defaultDataParser as DataParser<TData>);
78
-
79
- // Iterate through each event string
80
- for (const eventStr of eventStrings) {
81
- // Skip empty event strings (after trimming whitespace)
82
- if (!eventStr.trim()) continue;
83
-
84
- // Split the event string into lines (fields) using single newline
85
- const lines = eventStr.split('\n');
86
-
87
- // Use reduce to process each line in the event string
88
- const event = lines.reduce(
89
- (event, line) => {
90
- // Skip empty lines
91
- if (!line) return event;
92
-
93
- // Find the index of the first colon, which separates field name and value
94
- const colonIndex = line.indexOf(':');
95
-
96
- // Skip lines without a colon (invalid format)
97
- if (colonIndex === -1) return event;
98
-
99
- // Extract the field name (before colon) and trim whitespace
100
- const field = line.slice(0, colonIndex).trim();
101
- // Extract the field value (after colon) and trim whitespace
102
- const value = line.slice(colonIndex + 1).trim();
103
-
104
- // Handle the 'data' field specially, attempting JSON parsing
105
- if (field === 'data') {
106
- event.data = dataParser(value);
107
- } else {
108
- // For non-data fields, assign the value as a string to the event object
109
- (event as Record<string, unknown>)[field] = value;
110
- }
111
-
112
- return event;
113
- },
114
- {} as ServerSentEvent<TData>,
115
- );
116
-
117
- // Only emit the event to the results if it has at least one field
118
- if (Object.keys(event).length) {
119
- yield event;
120
- }
121
- }
122
- }
123
-
124
- async function* readStream<TData>(
125
- reader: ReadableStreamDefaultReader<Uint8Array>,
126
- options?: {
127
- dataParser?: DataParser<TData>;
128
- skipHeartbeats?: boolean;
129
- eventFilter?: string | string[];
130
- },
131
- ): AsyncGenerator<ServerSentEvent<TData>> {
132
- const skipHeartbeats = !!options?.skipHeartbeats;
133
-
134
- const eventFilter = options?.eventFilter
135
- ? Array.isArray(options.eventFilter)
136
- ? options.eventFilter
137
- : [options.eventFilter]
138
- : null;
139
-
140
- const decoder = new TextDecoder();
141
-
142
- // keep reading chunks from the stream until the reader signals completion
143
- while (true) {
144
- const { done, value } = await reader.read();
145
- // the underlying stream has ended, nothing more to read
146
- if (done) {
147
- // exit the read loop rather than yielding any further events
148
- break;
149
- }
150
-
151
- const text = decoder.decode(value, { stream: true });
152
- const events = parseEvents<TData>(text, { dataParser: options?.dataParser });
153
- // a chunk may contain multiple complete SSE events, emit each in turn
154
- for (const event of events) {
155
- // a retry directive reconfigures the reconnection delay rather than being a data event
156
- if (event.retry) {
157
- await new Promise((resolve) =>
158
- setTimeout(resolve, Number.parseInt(event.retry ?? '300', 10)),
159
- );
160
- // nothing to yield for a retry-only event
161
- continue;
162
- }
163
- // heartbeat filtering is opt-in via the skipHeartbeats option
164
- if (skipHeartbeats) {
165
- // Skip comment-based heartbeats (no event, data, or id)
166
- if (!event.event && !event.data && !event.id) {
167
- // this event carries no payload, so treat it as a heartbeat and skip it
168
- continue;
169
- }
170
- // Skip named heartbeat events (e.g., event: heartbeat or event: ping)
171
- if (event.event && ['heartbeat', 'ping'].includes(event.event)) {
172
- // an explicitly named heartbeat/ping event should also be skipped
173
- continue;
174
- }
175
- }
176
- // only emit events that pass the caller-provided event type filter, if any
177
- if (!eventFilter || (event.event && eventFilter.includes(event.event))) {
178
- yield event as ServerSentEvent<TData>;
179
- }
180
- }
181
- }
182
- }
183
-
184
- /**
185
- * Options for configuring the SSE (Server-Sent Events) selector.
186
- */
187
- export type SseSelectorOptions<TData = unknown> = {
188
- dataParser?: DataParser<TData>;
189
- /**
190
- * A string or an array of strings specifying the events to filter.
191
- * Only events matching the filter will be processed. If not provided,
192
- * all events will be processed.
193
- */
194
- eventFilter?: string | string[];
195
-
196
- /**
197
- * A boolean indicating whether to skip processing heartbeat events.
198
- * Defaults to `false` if not specified.
199
- */
200
- skipHeartbeats?: boolean;
201
-
202
- /**
203
- * An `AbortSignal` that can be used to abort the SSE operation.
204
- * Useful for managing the lifecycle of the SSE connection.
205
- */
206
- abortSignal?: AbortSignal | null;
207
- };
208
-
209
- /**
210
- * A type alias for selecting and transforming Server-Sent Events (SSE) from an HTTP response.
211
- *
212
- * @template TData - The type of the data contained within the Server-Sent Event. Defaults to `unknown`.
213
- * @template TResponse - The type of the HTTP response object. Defaults to `Response`.
214
- *
215
- * This selector is used to process and extract `ServerSentEvent<TData>` objects
216
- * from an HTTP `Response` object, enabling custom handling of SSE data streams.
217
- */
218
- export type SseSelector<TData = unknown, TResponse extends Response = Response> = ResponseSelector<
219
- ServerSentEvent<TData>,
220
- TResponse
221
- >;
222
-
223
- /**
224
- * Transforms an SSE response into an Observable stream of parsed events.
225
- * @param response - The HTTP response with Content-Type: text/event-stream.
226
- * @param options - Optional configuration for event filtering and heartbeat handling.
227
- * @param options.eventFilter - Filter events by type (single string or array).
228
- * @param options.skipHeartbeats - Skip empty/heartbeat events if true.
229
- * @param options.dataParser - Custom parser for the data field.
230
- * @param options.abortSignal - Abort signal to cancel the stream.
231
- * @returns An Observable emitting parsed ServerSentEvent objects.
232
- * @throws ServerSentEventResponseError if response is invalid or stream fails.
233
- */
234
- export const createSseSelector = <TData = unknown, TResponse extends Response = Response>(
235
- options?: SseSelectorOptions<TData>,
236
- ): SseSelector<TData, TResponse> => {
237
- return (response: TResponse): Observable<ServerSentEvent<TData>> => {
238
- // an unsuccessful HTTP status means there is no valid SSE stream to read
239
- if (!response.ok) {
240
- throw new ServerSentEventResponseError(`HTTP error! Status: ${response.status}`, response);
241
- }
242
-
243
- // a missing body means there is nothing to stream from
244
- if (!response.body) {
245
- throw new ServerSentEventResponseError('Response body is not readable', response);
246
- }
247
-
248
- // guard against consuming a non-SSE response as if it were an event stream
249
- if (!response.headers.get('Content-Type')?.includes('text/event-stream')) {
250
- throw new ServerSentEventResponseError('Response is not a text/event-stream', response);
251
- }
252
-
253
- const reader = response.body.getReader();
254
-
255
- return (
256
- from(
257
- readStream<TData>(reader, {
258
- dataParser: options?.dataParser,
259
- skipHeartbeats: options?.skipHeartbeats,
260
- eventFilter: options?.eventFilter,
261
- }),
262
- )
263
- // stop the stream on abort and always release the underlying reader when done
264
- .pipe(
265
- // Stop reading if the abort signal is triggered
266
- takeUntil(options?.abortSignal ? fromEvent(options.abortSignal, 'abort') : EMPTY),
267
- finalize(async () => {
268
- // cancel just in case of a pre-mature exit
269
- await reader.cancel().catch(() => {
270
- /** ignore cancellation errors */
271
- });
272
- reader.releaseLock();
273
- }),
274
- )
275
- );
276
- };
277
- };
278
-
279
- export default createSseSelector;
@@ -1,11 +0,0 @@
1
- export { jsonSelector } from './json-selector';
2
- export { blobSelector } from './blob-selector';
3
- export { createSseSelector } from './create-sse-selector';
4
-
5
- export type { ResponseSelector } from '../client/types';
6
- export type {
7
- DataParser,
8
- ServerSentEvent,
9
- SseSelector,
10
- SseSelectorOptions,
11
- } from './create-sse-selector';
@@ -1,52 +0,0 @@
1
- import type { ResponseSelector } from '../client/types';
2
- import { HttpJsonResponseError } from '../../errors/index.js';
3
-
4
- /**
5
- * Asynchronously parses the JSON data from a given HTTP response.
6
- *
7
- * If the response has a status code of 204 (No Content), this function will resolve with `undefined`.
8
- *
9
- * If the response is not successful (i.e. `response.ok` is `false`), this function will throw an `HttpJsonResponseError` with the response details and the parsed data (if any).
10
- *
11
- * If there is an error parsing the JSON data, this function will throw an `HttpJsonResponseError` with the parsing error and the original response.
12
- *
13
- * @template TType - The expected type of the parsed JSON data.
14
- * @template TResponse - The type of the HTTP response object.
15
- * @param response - The HTTP response to parse.
16
- * @returns A promise that resolves with the parsed JSON data, or rejects with an `HttpJsonResponseError`.
17
- */
18
- export const jsonSelector: ResponseSelector = async <
19
- TType = unknown,
20
- TResponse extends Response = Response,
21
- >(
22
- response: TResponse,
23
- ): Promise<TType> => {
24
- /** Status code 204 indicates no content in the response */
25
- if (response.status === 204) {
26
- return Promise.resolve() as Promise<TType>;
27
- }
28
-
29
- try {
30
- // Parse the response JSON data
31
- const data = await response.json();
32
-
33
- // Check if the response was successful
34
- if (!response.ok) {
35
- // Throw an error with the response details
36
- throw new HttpJsonResponseError('network response was not OK', response, { data });
37
- }
38
-
39
- // Return the parsed data
40
- return data;
41
- } catch (cause) {
42
- // If the cause is an HttpJsonResponseError, rethrow it
43
- if (cause instanceof HttpJsonResponseError) {
44
- throw cause;
45
- }
46
-
47
- // Otherwise, throw a new HttpJsonResponseError with the parsing error
48
- throw new HttpJsonResponseError('failed to parse response', response, { cause });
49
- }
50
- };
51
-
52
- export default jsonSelector;
@@ -1,40 +0,0 @@
1
- import type { HttpMiddleware } from '../lib/operators/types';
2
-
3
- import { resolveOpenApiMockResponse, type OpenApiMockLike } from './resolve-open-api-mock-response';
4
-
5
- /**
6
- * Adapts an `OpenApiMock` into an {@link HttpMiddleware}, so
7
- * `configurator.http.addMiddleware(...)` fakes every matching request
8
- * straight from an OpenAPI document — no separate mock configurator needed.
9
- *
10
- * @remarks
11
- * A request that matches no operation in the document falls through to
12
- * `next`, so this composes with whatever else is registered — including the
13
- * real network call, or another middleware further down the chain. Because
14
- * `addMiddleware` wraps `_performFetch` rather than replacing it, the exact
15
- * same registration also fakes requests through any client this configurator
16
- * builds, so app config never has to branch on whether it's under test.
17
- *
18
- * @param openApiMock - Typically `createOpenApiMock(document)` from `@equinor/fusion-openapi-mock`.
19
- * @returns A middleware for {@link IHttpClientConfigurator.addMiddleware}.
20
- *
21
- * @example Fake every operation in a spec, straight from the document
22
- * ```typescript
23
- * import { createOpenApiMock } from '@equinor/fusion-openapi-mock';
24
- * import openapi from './openapi.json' with { type: 'json' };
25
- *
26
- * configurator.http.addMiddleware(createOpenApiMockMiddleware(createOpenApiMock(openapi)));
27
- * ```
28
- */
29
- export function createOpenApiMockMiddleware(openApiMock: OpenApiMockLike): HttpMiddleware {
30
- return async (uri, init, next) => {
31
- const response = await resolveOpenApiMockResponse(
32
- openApiMock,
33
- init.method ?? 'GET',
34
- new URL(uri),
35
- );
36
- return response ?? next(uri, init);
37
- };
38
- }
39
-
40
- export default createOpenApiMockMiddleware;