api-def 0.14.3 → 0.15.0-alpha.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/cjs/ApiConstants.d.ts +1 -0
- package/cjs/ApiConstants.js +1 -0
- package/cjs/EndpointBuilder.d.ts +1 -1
- package/cjs/Requester.js +2 -1
- package/cjs/backend/AxiosRequestBackend.d.ts +9 -5
- package/cjs/backend/AxiosRequestBackend.js +30 -9
- package/cjs/backend/FetchRequestBackend.d.ts +9 -5
- package/cjs/backend/FetchRequestBackend.js +17 -2
- package/cjs/backend/WebSocketRequest.d.ts +11 -0
- package/cjs/backend/WebSocketRequest.js +36 -0
- package/esm/ApiConstants.d.ts +1 -0
- package/esm/ApiConstants.js +1 -0
- package/esm/EndpointBuilder.d.ts +1 -1
- package/esm/Requester.js +2 -1
- package/esm/backend/AxiosRequestBackend.d.ts +9 -5
- package/esm/backend/AxiosRequestBackend.js +30 -9
- package/esm/backend/FetchRequestBackend.d.ts +9 -5
- package/esm/backend/FetchRequestBackend.js +17 -2
- package/esm/backend/WebSocketRequest.d.ts +11 -0
- package/esm/backend/WebSocketRequest.js +31 -0
- package/package.json +1 -1
package/cjs/ApiConstants.d.ts
CHANGED
package/cjs/ApiConstants.js
CHANGED
package/cjs/EndpointBuilder.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { ResponseType } from "./ApiConstants";
|
|
|
4
4
|
import type { Body, Params, Query, RawHeaders, State } from "./ApiTypes";
|
|
5
5
|
import Endpoint, { type EndpointOptions } from "./Endpoint";
|
|
6
6
|
import type { BodyValidationOptions, ValidationOptions } from "./Validation";
|
|
7
|
-
type DefaultResponseOf<T extends ResponseType | undefined> = T extends "text" ? string : T extends "arraybuffer" ? ArrayBuffer : "stream"
|
|
7
|
+
type DefaultResponseOf<T extends ResponseType | undefined> = T extends "text" ? string : T extends "arraybuffer" ? ArrayBuffer : T extends "stream" ? AsyncIterable<Uint8Array> : T extends "websocket" ? WebSocket : unknown;
|
|
8
8
|
export type EndpointBuildOptions<TResponse = unknown, TParams extends Params | undefined = undefined, TQuery extends Query | undefined = undefined, TBody extends Body | undefined = undefined, TState extends State = State, TRequestHeaders extends RawHeaders | undefined = RawHeaders | undefined, TResponseHeaders extends RawHeaders | undefined = RawHeaders | undefined, TPath extends string = string, TResponseType extends ResponseType | undefined = undefined> = Omit<EndpointOptions<TResponse, TParams, TQuery, TBody, TState, TPath, TRequestHeaders, TResponseHeaders>, "validation"> & {
|
|
9
9
|
responseType?: TResponseType;
|
|
10
10
|
};
|
package/cjs/Requester.js
CHANGED
|
@@ -112,7 +112,8 @@ const makeRequest = async (context) => {
|
|
|
112
112
|
context.addCanceller(canceler);
|
|
113
113
|
const response = await promise;
|
|
114
114
|
const parsedResponse = (await parseResponse(context, response));
|
|
115
|
-
if (!(
|
|
115
|
+
if (!(context.responseType === "websocket" && parsedResponse.status === 101) &&
|
|
116
|
+
!(0, ApiUtils_1.isAcceptableStatus)(parsedResponse.status, context.requestConfig.acceptableStatus)) {
|
|
116
117
|
throw (0, RequestError_1.convertToRequestError)({
|
|
117
118
|
error: new Error(`[api-def] Invalid response status code '${parsedResponse.status}'`),
|
|
118
119
|
response: parsedResponse,
|
|
@@ -3,12 +3,16 @@ import type { ApiResponse } from "../ApiTypes";
|
|
|
3
3
|
import type RequestContext from "../RequestContext";
|
|
4
4
|
import type RequestBackend from "./RequestBackend";
|
|
5
5
|
import type { ConvertedApiResponse, RequestBackendErrorInfo, RequestOperation } from "./RequestBackend";
|
|
6
|
+
import { type WebSocketConstructor, type WebSocketResponse } from "./WebSocketRequest";
|
|
6
7
|
export declare const isAxiosError: (error: Error) => error is AxiosError;
|
|
7
|
-
|
|
8
|
+
type AxiosBackendResponse = AxiosResponse | WebSocketResponse;
|
|
9
|
+
export default class AxiosRequestBackend implements RequestBackend<AxiosBackendResponse> {
|
|
8
10
|
readonly id = "axios";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
webSocketConstructor: WebSocketConstructor | undefined;
|
|
12
|
+
constructor(axiosLibrary: any, webSocketConstructor?: WebSocketConstructor);
|
|
13
|
+
extractResponseFromError(error: Error): Promise<AxiosBackendResponse | null | undefined>;
|
|
14
|
+
convertResponse<T>(context: RequestContext, response: AxiosBackendResponse): Promise<ConvertedApiResponse<T>>;
|
|
15
|
+
makeRequest(context: RequestContext): RequestOperation<AxiosBackendResponse>;
|
|
13
16
|
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
14
17
|
}
|
|
18
|
+
export {};
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isAxiosError = void 0;
|
|
4
4
|
const ApiUtils_1 = require("../ApiUtils");
|
|
5
|
+
const WebSocketRequest_1 = require("./WebSocketRequest");
|
|
5
6
|
let axios;
|
|
6
7
|
const isAxiosError = (error) => {
|
|
7
8
|
return "isAxiosError" in error;
|
|
@@ -32,9 +33,13 @@ const getCacheHeaders = (browserCache) => {
|
|
|
32
33
|
}
|
|
33
34
|
};
|
|
34
35
|
class AxiosRequestBackend {
|
|
35
|
-
constructor(axiosLibrary) {
|
|
36
|
+
constructor(axiosLibrary, webSocketConstructor) {
|
|
36
37
|
this.id = "axios";
|
|
38
|
+
this.webSocketConstructor = (0, WebSocketRequest_1.getGlobalWebSocketConstructor)();
|
|
37
39
|
axios = axiosLibrary;
|
|
40
|
+
if (webSocketConstructor !== undefined) {
|
|
41
|
+
this.webSocketConstructor = webSocketConstructor;
|
|
42
|
+
}
|
|
38
43
|
}
|
|
39
44
|
async extractResponseFromError(error) {
|
|
40
45
|
if ((0, exports.isAxiosError)(error)) {
|
|
@@ -43,17 +48,30 @@ class AxiosRequestBackend {
|
|
|
43
48
|
return undefined;
|
|
44
49
|
}
|
|
45
50
|
async convertResponse(context, response) {
|
|
46
|
-
|
|
51
|
+
if (context.responseType === "websocket") {
|
|
52
|
+
const webSocketResponse = response;
|
|
53
|
+
return {
|
|
54
|
+
method: context.method,
|
|
55
|
+
url: webSocketResponse.url,
|
|
56
|
+
data: webSocketResponse.webSocket,
|
|
57
|
+
headers: webSocketResponse.headers,
|
|
58
|
+
status: webSocketResponse.status,
|
|
59
|
+
state: context.requestConfig.state,
|
|
60
|
+
stats: context.stats,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const axiosResponse = response;
|
|
64
|
+
const contentType = axiosResponse.headers["content-type"];
|
|
47
65
|
const responseType = context.responseType ?? (0, ApiUtils_1.inferResponseType)(contentType == null ? contentType : String(contentType));
|
|
48
66
|
let data;
|
|
49
67
|
if (responseType === "stream") {
|
|
50
68
|
// For streaming responses, we create an async iterator from the response data
|
|
51
|
-
if (!
|
|
69
|
+
if (!axiosResponse.data) {
|
|
52
70
|
throw new Error("[api-def] Response data is null for streaming response");
|
|
53
71
|
}
|
|
54
72
|
data = {
|
|
55
73
|
async *[Symbol.asyncIterator]() {
|
|
56
|
-
const stream =
|
|
74
|
+
const stream = axiosResponse.data;
|
|
57
75
|
if (stream[Symbol.asyncIterator]) {
|
|
58
76
|
yield* stream;
|
|
59
77
|
}
|
|
@@ -68,20 +86,23 @@ class AxiosRequestBackend {
|
|
|
68
86
|
};
|
|
69
87
|
}
|
|
70
88
|
else {
|
|
71
|
-
data =
|
|
89
|
+
data = axiosResponse.data;
|
|
72
90
|
}
|
|
73
91
|
return {
|
|
74
92
|
method: context.method,
|
|
75
|
-
url:
|
|
93
|
+
url: axiosResponse.request.res?.responseUrl ?? axiosResponse.request?._redirectable?._currentUrl,
|
|
76
94
|
data: data,
|
|
77
|
-
headers:
|
|
78
|
-
status:
|
|
95
|
+
headers: axiosResponse.headers,
|
|
96
|
+
status: axiosResponse.status,
|
|
79
97
|
state: context.requestConfig.state,
|
|
80
|
-
__lowercaseHeaders:
|
|
98
|
+
__lowercaseHeaders: axiosResponse._lowerCaseResponseHeaders,
|
|
81
99
|
stats: context.stats,
|
|
82
100
|
};
|
|
83
101
|
}
|
|
84
102
|
makeRequest(context) {
|
|
103
|
+
if (context.responseType === "websocket") {
|
|
104
|
+
return (0, WebSocketRequest_1.makeWebSocketRequest)(context, this.webSocketConstructor);
|
|
105
|
+
}
|
|
85
106
|
const { requestConfig } = context;
|
|
86
107
|
const url = context.requestUrl;
|
|
87
108
|
let canceler = null;
|
|
@@ -3,14 +3,18 @@ import type RequestContext from "../RequestContext";
|
|
|
3
3
|
import { type Fetch } from "../Utils";
|
|
4
4
|
import type RequestBackend from "./RequestBackend";
|
|
5
5
|
import type { ConvertedApiResponse, RequestBackendErrorInfo, RequestOperation } from "./RequestBackend";
|
|
6
|
-
|
|
6
|
+
import { type WebSocketConstructor, type WebSocketResponse } from "./WebSocketRequest";
|
|
7
|
+
type FetchBackendResponse = Response | WebSocketResponse;
|
|
8
|
+
export default class FetchRequestBackend implements RequestBackend<FetchBackendResponse> {
|
|
7
9
|
fetch: (((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) & typeof fetch) | undefined;
|
|
10
|
+
webSocketConstructor: WebSocketConstructor | undefined;
|
|
8
11
|
readonly id = "fetch";
|
|
9
|
-
constructor(fetchLibrary?: Fetch);
|
|
10
|
-
extractResponseFromError(error: Error): Promise<
|
|
11
|
-
convertResponse<T>(context: RequestContext, response:
|
|
12
|
+
constructor(fetchLibrary?: Fetch, webSocketConstructor?: WebSocketConstructor);
|
|
13
|
+
extractResponseFromError(error: Error): Promise<FetchBackendResponse | null | undefined>;
|
|
14
|
+
convertResponse<T>(context: RequestContext, response: FetchBackendResponse & {
|
|
12
15
|
__text?: string;
|
|
13
16
|
}): Promise<ConvertedApiResponse<T>>;
|
|
14
|
-
makeRequest(context: RequestContext): RequestOperation<
|
|
17
|
+
makeRequest(context: RequestContext): RequestOperation<FetchBackendResponse>;
|
|
15
18
|
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
16
19
|
}
|
|
20
|
+
export {};
|
|
@@ -37,11 +37,13 @@ const ApiUtils_1 = require("../ApiUtils");
|
|
|
37
37
|
const RequestError_1 = require("../RequestError");
|
|
38
38
|
const Utils = __importStar(require("../Utils"));
|
|
39
39
|
const Utils_1 = require("../Utils");
|
|
40
|
+
const WebSocketRequest_1 = require("./WebSocketRequest");
|
|
40
41
|
class FetchError extends Error {
|
|
41
42
|
}
|
|
42
43
|
class FetchRequestBackend {
|
|
43
|
-
constructor(fetchLibrary) {
|
|
44
|
+
constructor(fetchLibrary, webSocketConstructor) {
|
|
44
45
|
this.fetch = (0, Utils_1.getGlobalFetch)();
|
|
46
|
+
this.webSocketConstructor = (0, WebSocketRequest_1.getGlobalWebSocketConstructor)();
|
|
45
47
|
this.id = "fetch";
|
|
46
48
|
if (fetchLibrary !== undefined) {
|
|
47
49
|
this.fetch = fetchLibrary;
|
|
@@ -50,6 +52,9 @@ class FetchRequestBackend {
|
|
|
50
52
|
this.fetch = fetchLibrary.bind((0, Utils_1.getGlobal)());
|
|
51
53
|
}
|
|
52
54
|
}
|
|
55
|
+
if (webSocketConstructor !== undefined) {
|
|
56
|
+
this.webSocketConstructor = webSocketConstructor;
|
|
57
|
+
}
|
|
53
58
|
}
|
|
54
59
|
async extractResponseFromError(error) {
|
|
55
60
|
if ("response" in error) {
|
|
@@ -73,7 +78,13 @@ class FetchRequestBackend {
|
|
|
73
78
|
let text;
|
|
74
79
|
let data;
|
|
75
80
|
try {
|
|
76
|
-
if (responseType === "
|
|
81
|
+
if (responseType === "websocket") {
|
|
82
|
+
data = response.webSocket;
|
|
83
|
+
if (!data) {
|
|
84
|
+
throw new Error("[api-def] WebSocket response did not include a webSocket");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
else if (responseType === "arraybuffer") {
|
|
77
88
|
data = await response.arrayBuffer();
|
|
78
89
|
}
|
|
79
90
|
else if (responseType === "json") {
|
|
@@ -110,6 +121,10 @@ class FetchRequestBackend {
|
|
|
110
121
|
const abortSignal = abortController ? abortController.signal : undefined;
|
|
111
122
|
let softAbort = false;
|
|
112
123
|
let responded = false;
|
|
124
|
+
if (context.responseType === "websocket") {
|
|
125
|
+
responded = true;
|
|
126
|
+
return (0, WebSocketRequest_1.makeWebSocketRequest)(context, this.webSocketConstructor);
|
|
127
|
+
}
|
|
113
128
|
const body = context.getParsedBody();
|
|
114
129
|
const bodyJsonify = body !== null && typeof body === "object" && !Utils.isFormDataLike(body) && !(body instanceof URLSearchParams);
|
|
115
130
|
const headers = Utils.assign({
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type RequestContext from "../RequestContext";
|
|
2
|
+
import type { RequestOperation } from "./RequestBackend";
|
|
3
|
+
export type WebSocketConstructor = new (url: string | URL, protocols?: string | string[]) => WebSocket;
|
|
4
|
+
export type WebSocketResponse = {
|
|
5
|
+
status: 101;
|
|
6
|
+
headers: Headers;
|
|
7
|
+
url: string;
|
|
8
|
+
webSocket: WebSocket;
|
|
9
|
+
};
|
|
10
|
+
export declare const getGlobalWebSocketConstructor: () => WebSocketConstructor | undefined;
|
|
11
|
+
export declare const makeWebSocketRequest: (context: RequestContext, webSocketConstructor: WebSocketConstructor | undefined) => RequestOperation<WebSocketResponse>;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makeWebSocketRequest = exports.getGlobalWebSocketConstructor = void 0;
|
|
4
|
+
const getGlobalWebSocketConstructor = () => {
|
|
5
|
+
return typeof WebSocket !== "undefined" ? WebSocket : undefined;
|
|
6
|
+
};
|
|
7
|
+
exports.getGlobalWebSocketConstructor = getGlobalWebSocketConstructor;
|
|
8
|
+
const makeWebSocketRequest = (context, webSocketConstructor) => {
|
|
9
|
+
if (!webSocketConstructor) {
|
|
10
|
+
throw new Error("[api-def] No WebSocket constructor was provided");
|
|
11
|
+
}
|
|
12
|
+
const url = context.requestUrl;
|
|
13
|
+
if (url.protocol === "http:") {
|
|
14
|
+
url.protocol = "ws:";
|
|
15
|
+
}
|
|
16
|
+
else if (url.protocol === "https:") {
|
|
17
|
+
url.protocol = "wss:";
|
|
18
|
+
}
|
|
19
|
+
const protocolHeader = context.requestConfig.headers?.["Sec-WebSocket-Protocol"];
|
|
20
|
+
const protocols = typeof protocolHeader === "string"
|
|
21
|
+
? protocolHeader.split(",").map((protocol) => protocol.trim())
|
|
22
|
+
: typeof protocolHeader === "number" || typeof protocolHeader === "boolean"
|
|
23
|
+
? String(protocolHeader)
|
|
24
|
+
: undefined;
|
|
25
|
+
const webSocket = new webSocketConstructor(url.href, protocols);
|
|
26
|
+
return {
|
|
27
|
+
promise: Promise.resolve({
|
|
28
|
+
status: 101,
|
|
29
|
+
headers: new Headers(),
|
|
30
|
+
url: url.href,
|
|
31
|
+
webSocket,
|
|
32
|
+
}),
|
|
33
|
+
canceler: () => webSocket.close(),
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
exports.makeWebSocketRequest = makeWebSocketRequest;
|
package/esm/ApiConstants.d.ts
CHANGED
package/esm/ApiConstants.js
CHANGED
package/esm/EndpointBuilder.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type { ResponseType } from "./ApiConstants.js";
|
|
|
4
4
|
import type { Body, Params, Query, RawHeaders, State } from "./ApiTypes.js";
|
|
5
5
|
import Endpoint, { type EndpointOptions } from "./Endpoint.js";
|
|
6
6
|
import type { BodyValidationOptions, ValidationOptions } from "./Validation.js";
|
|
7
|
-
type DefaultResponseOf<T extends ResponseType | undefined> = T extends "text" ? string : T extends "arraybuffer" ? ArrayBuffer : "stream"
|
|
7
|
+
type DefaultResponseOf<T extends ResponseType | undefined> = T extends "text" ? string : T extends "arraybuffer" ? ArrayBuffer : T extends "stream" ? AsyncIterable<Uint8Array> : T extends "websocket" ? WebSocket : unknown;
|
|
8
8
|
export type EndpointBuildOptions<TResponse = unknown, TParams extends Params | undefined = undefined, TQuery extends Query | undefined = undefined, TBody extends Body | undefined = undefined, TState extends State = State, TRequestHeaders extends RawHeaders | undefined = RawHeaders | undefined, TResponseHeaders extends RawHeaders | undefined = RawHeaders | undefined, TPath extends string = string, TResponseType extends ResponseType | undefined = undefined> = Omit<EndpointOptions<TResponse, TParams, TQuery, TBody, TState, TPath, TRequestHeaders, TResponseHeaders>, "validation"> & {
|
|
9
9
|
responseType?: TResponseType;
|
|
10
10
|
};
|
package/esm/Requester.js
CHANGED
|
@@ -105,7 +105,8 @@ const makeRequest = async (context) => {
|
|
|
105
105
|
context.addCanceller(canceler);
|
|
106
106
|
const response = await promise;
|
|
107
107
|
const parsedResponse = (await parseResponse(context, response));
|
|
108
|
-
if (!
|
|
108
|
+
if (!(context.responseType === "websocket" && parsedResponse.status === 101) &&
|
|
109
|
+
!isAcceptableStatus(parsedResponse.status, context.requestConfig.acceptableStatus)) {
|
|
109
110
|
throw convertToRequestError({
|
|
110
111
|
error: new Error(`[api-def] Invalid response status code '${parsedResponse.status}'`),
|
|
111
112
|
response: parsedResponse,
|
|
@@ -3,12 +3,16 @@ import type { ApiResponse } from "../ApiTypes.js";
|
|
|
3
3
|
import type RequestContext from "../RequestContext.js";
|
|
4
4
|
import type RequestBackend from "./RequestBackend.js";
|
|
5
5
|
import type { ConvertedApiResponse, RequestBackendErrorInfo, RequestOperation } from "./RequestBackend.js";
|
|
6
|
+
import { type WebSocketConstructor, type WebSocketResponse } from "./WebSocketRequest.js";
|
|
6
7
|
export declare const isAxiosError: (error: Error) => error is AxiosError;
|
|
7
|
-
|
|
8
|
+
type AxiosBackendResponse = AxiosResponse | WebSocketResponse;
|
|
9
|
+
export default class AxiosRequestBackend implements RequestBackend<AxiosBackendResponse> {
|
|
8
10
|
readonly id = "axios";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
webSocketConstructor: WebSocketConstructor | undefined;
|
|
12
|
+
constructor(axiosLibrary: any, webSocketConstructor?: WebSocketConstructor);
|
|
13
|
+
extractResponseFromError(error: Error): Promise<AxiosBackendResponse | null | undefined>;
|
|
14
|
+
convertResponse<T>(context: RequestContext, response: AxiosBackendResponse): Promise<ConvertedApiResponse<T>>;
|
|
15
|
+
makeRequest(context: RequestContext): RequestOperation<AxiosBackendResponse>;
|
|
13
16
|
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
14
17
|
}
|
|
18
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { inferResponseType } from "../ApiUtils.js";
|
|
2
|
+
import { getGlobalWebSocketConstructor, makeWebSocketRequest, } from "./WebSocketRequest.js";
|
|
2
3
|
let axios;
|
|
3
4
|
export const isAxiosError = (error) => {
|
|
4
5
|
return "isAxiosError" in error;
|
|
@@ -28,9 +29,13 @@ const getCacheHeaders = (browserCache) => {
|
|
|
28
29
|
}
|
|
29
30
|
};
|
|
30
31
|
export default class AxiosRequestBackend {
|
|
31
|
-
constructor(axiosLibrary) {
|
|
32
|
+
constructor(axiosLibrary, webSocketConstructor) {
|
|
32
33
|
this.id = "axios";
|
|
34
|
+
this.webSocketConstructor = getGlobalWebSocketConstructor();
|
|
33
35
|
axios = axiosLibrary;
|
|
36
|
+
if (webSocketConstructor !== undefined) {
|
|
37
|
+
this.webSocketConstructor = webSocketConstructor;
|
|
38
|
+
}
|
|
34
39
|
}
|
|
35
40
|
async extractResponseFromError(error) {
|
|
36
41
|
if (isAxiosError(error)) {
|
|
@@ -39,17 +44,30 @@ export default class AxiosRequestBackend {
|
|
|
39
44
|
return undefined;
|
|
40
45
|
}
|
|
41
46
|
async convertResponse(context, response) {
|
|
42
|
-
|
|
47
|
+
if (context.responseType === "websocket") {
|
|
48
|
+
const webSocketResponse = response;
|
|
49
|
+
return {
|
|
50
|
+
method: context.method,
|
|
51
|
+
url: webSocketResponse.url,
|
|
52
|
+
data: webSocketResponse.webSocket,
|
|
53
|
+
headers: webSocketResponse.headers,
|
|
54
|
+
status: webSocketResponse.status,
|
|
55
|
+
state: context.requestConfig.state,
|
|
56
|
+
stats: context.stats,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const axiosResponse = response;
|
|
60
|
+
const contentType = axiosResponse.headers["content-type"];
|
|
43
61
|
const responseType = context.responseType ?? inferResponseType(contentType == null ? contentType : String(contentType));
|
|
44
62
|
let data;
|
|
45
63
|
if (responseType === "stream") {
|
|
46
64
|
// For streaming responses, we create an async iterator from the response data
|
|
47
|
-
if (!
|
|
65
|
+
if (!axiosResponse.data) {
|
|
48
66
|
throw new Error("[api-def] Response data is null for streaming response");
|
|
49
67
|
}
|
|
50
68
|
data = {
|
|
51
69
|
async *[Symbol.asyncIterator]() {
|
|
52
|
-
const stream =
|
|
70
|
+
const stream = axiosResponse.data;
|
|
53
71
|
if (stream[Symbol.asyncIterator]) {
|
|
54
72
|
yield* stream;
|
|
55
73
|
}
|
|
@@ -64,20 +82,23 @@ export default class AxiosRequestBackend {
|
|
|
64
82
|
};
|
|
65
83
|
}
|
|
66
84
|
else {
|
|
67
|
-
data =
|
|
85
|
+
data = axiosResponse.data;
|
|
68
86
|
}
|
|
69
87
|
return {
|
|
70
88
|
method: context.method,
|
|
71
|
-
url:
|
|
89
|
+
url: axiosResponse.request.res?.responseUrl ?? axiosResponse.request?._redirectable?._currentUrl,
|
|
72
90
|
data: data,
|
|
73
|
-
headers:
|
|
74
|
-
status:
|
|
91
|
+
headers: axiosResponse.headers,
|
|
92
|
+
status: axiosResponse.status,
|
|
75
93
|
state: context.requestConfig.state,
|
|
76
|
-
__lowercaseHeaders:
|
|
94
|
+
__lowercaseHeaders: axiosResponse._lowerCaseResponseHeaders,
|
|
77
95
|
stats: context.stats,
|
|
78
96
|
};
|
|
79
97
|
}
|
|
80
98
|
makeRequest(context) {
|
|
99
|
+
if (context.responseType === "websocket") {
|
|
100
|
+
return makeWebSocketRequest(context, this.webSocketConstructor);
|
|
101
|
+
}
|
|
81
102
|
const { requestConfig } = context;
|
|
82
103
|
const url = context.requestUrl;
|
|
83
104
|
let canceler = null;
|
|
@@ -3,14 +3,18 @@ import type RequestContext from "../RequestContext.js";
|
|
|
3
3
|
import { type Fetch } from "../Utils.js";
|
|
4
4
|
import type RequestBackend from "./RequestBackend.js";
|
|
5
5
|
import type { ConvertedApiResponse, RequestBackendErrorInfo, RequestOperation } from "./RequestBackend.js";
|
|
6
|
-
|
|
6
|
+
import { type WebSocketConstructor, type WebSocketResponse } from "./WebSocketRequest.js";
|
|
7
|
+
type FetchBackendResponse = Response | WebSocketResponse;
|
|
8
|
+
export default class FetchRequestBackend implements RequestBackend<FetchBackendResponse> {
|
|
7
9
|
fetch: (((input: RequestInfo | URL, init?: RequestInit) => Promise<Response>) & typeof fetch) | undefined;
|
|
10
|
+
webSocketConstructor: WebSocketConstructor | undefined;
|
|
8
11
|
readonly id = "fetch";
|
|
9
|
-
constructor(fetchLibrary?: Fetch);
|
|
10
|
-
extractResponseFromError(error: Error): Promise<
|
|
11
|
-
convertResponse<T>(context: RequestContext, response:
|
|
12
|
+
constructor(fetchLibrary?: Fetch, webSocketConstructor?: WebSocketConstructor);
|
|
13
|
+
extractResponseFromError(error: Error): Promise<FetchBackendResponse | null | undefined>;
|
|
14
|
+
convertResponse<T>(context: RequestContext, response: FetchBackendResponse & {
|
|
12
15
|
__text?: string;
|
|
13
16
|
}): Promise<ConvertedApiResponse<T>>;
|
|
14
|
-
makeRequest(context: RequestContext): RequestOperation<
|
|
17
|
+
makeRequest(context: RequestContext): RequestOperation<FetchBackendResponse>;
|
|
15
18
|
getErrorInfo(_error: Error, _response: ApiResponse | undefined | null): RequestBackendErrorInfo | undefined;
|
|
16
19
|
}
|
|
20
|
+
export {};
|
|
@@ -2,11 +2,13 @@ import { inferResponseType } from "../ApiUtils.js";
|
|
|
2
2
|
import { convertToRequestError, RequestErrorCode } from "../RequestError.js";
|
|
3
3
|
import * as Utils from "../Utils.js";
|
|
4
4
|
import { getGlobal, getGlobalFetch } from "../Utils.js";
|
|
5
|
+
import { getGlobalWebSocketConstructor, makeWebSocketRequest, } from "./WebSocketRequest.js";
|
|
5
6
|
class FetchError extends Error {
|
|
6
7
|
}
|
|
7
8
|
export default class FetchRequestBackend {
|
|
8
|
-
constructor(fetchLibrary) {
|
|
9
|
+
constructor(fetchLibrary, webSocketConstructor) {
|
|
9
10
|
this.fetch = getGlobalFetch();
|
|
11
|
+
this.webSocketConstructor = getGlobalWebSocketConstructor();
|
|
10
12
|
this.id = "fetch";
|
|
11
13
|
if (fetchLibrary !== undefined) {
|
|
12
14
|
this.fetch = fetchLibrary;
|
|
@@ -15,6 +17,9 @@ export default class FetchRequestBackend {
|
|
|
15
17
|
this.fetch = fetchLibrary.bind(getGlobal());
|
|
16
18
|
}
|
|
17
19
|
}
|
|
20
|
+
if (webSocketConstructor !== undefined) {
|
|
21
|
+
this.webSocketConstructor = webSocketConstructor;
|
|
22
|
+
}
|
|
18
23
|
}
|
|
19
24
|
async extractResponseFromError(error) {
|
|
20
25
|
if ("response" in error) {
|
|
@@ -38,7 +43,13 @@ export default class FetchRequestBackend {
|
|
|
38
43
|
let text;
|
|
39
44
|
let data;
|
|
40
45
|
try {
|
|
41
|
-
if (responseType === "
|
|
46
|
+
if (responseType === "websocket") {
|
|
47
|
+
data = response.webSocket;
|
|
48
|
+
if (!data) {
|
|
49
|
+
throw new Error("[api-def] WebSocket response did not include a webSocket");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
else if (responseType === "arraybuffer") {
|
|
42
53
|
data = await response.arrayBuffer();
|
|
43
54
|
}
|
|
44
55
|
else if (responseType === "json") {
|
|
@@ -75,6 +86,10 @@ export default class FetchRequestBackend {
|
|
|
75
86
|
const abortSignal = abortController ? abortController.signal : undefined;
|
|
76
87
|
let softAbort = false;
|
|
77
88
|
let responded = false;
|
|
89
|
+
if (context.responseType === "websocket") {
|
|
90
|
+
responded = true;
|
|
91
|
+
return makeWebSocketRequest(context, this.webSocketConstructor);
|
|
92
|
+
}
|
|
78
93
|
const body = context.getParsedBody();
|
|
79
94
|
const bodyJsonify = body !== null && typeof body === "object" && !Utils.isFormDataLike(body) && !(body instanceof URLSearchParams);
|
|
80
95
|
const headers = Utils.assign({
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type RequestContext from "../RequestContext.js";
|
|
2
|
+
import type { RequestOperation } from "./RequestBackend.js";
|
|
3
|
+
export type WebSocketConstructor = new (url: string | URL, protocols?: string | string[]) => WebSocket;
|
|
4
|
+
export type WebSocketResponse = {
|
|
5
|
+
status: 101;
|
|
6
|
+
headers: Headers;
|
|
7
|
+
url: string;
|
|
8
|
+
webSocket: WebSocket;
|
|
9
|
+
};
|
|
10
|
+
export declare const getGlobalWebSocketConstructor: () => WebSocketConstructor | undefined;
|
|
11
|
+
export declare const makeWebSocketRequest: (context: RequestContext, webSocketConstructor: WebSocketConstructor | undefined) => RequestOperation<WebSocketResponse>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export const getGlobalWebSocketConstructor = () => {
|
|
2
|
+
return typeof WebSocket !== "undefined" ? WebSocket : undefined;
|
|
3
|
+
};
|
|
4
|
+
export const makeWebSocketRequest = (context, webSocketConstructor) => {
|
|
5
|
+
if (!webSocketConstructor) {
|
|
6
|
+
throw new Error("[api-def] No WebSocket constructor was provided");
|
|
7
|
+
}
|
|
8
|
+
const url = context.requestUrl;
|
|
9
|
+
if (url.protocol === "http:") {
|
|
10
|
+
url.protocol = "ws:";
|
|
11
|
+
}
|
|
12
|
+
else if (url.protocol === "https:") {
|
|
13
|
+
url.protocol = "wss:";
|
|
14
|
+
}
|
|
15
|
+
const protocolHeader = context.requestConfig.headers?.["Sec-WebSocket-Protocol"];
|
|
16
|
+
const protocols = typeof protocolHeader === "string"
|
|
17
|
+
? protocolHeader.split(",").map((protocol) => protocol.trim())
|
|
18
|
+
: typeof protocolHeader === "number" || typeof protocolHeader === "boolean"
|
|
19
|
+
? String(protocolHeader)
|
|
20
|
+
: undefined;
|
|
21
|
+
const webSocket = new webSocketConstructor(url.href, protocols);
|
|
22
|
+
return {
|
|
23
|
+
promise: Promise.resolve({
|
|
24
|
+
status: 101,
|
|
25
|
+
headers: new Headers(),
|
|
26
|
+
url: url.href,
|
|
27
|
+
webSocket,
|
|
28
|
+
}),
|
|
29
|
+
canceler: () => webSocket.close(),
|
|
30
|
+
};
|
|
31
|
+
};
|