api-def 0.15.0-alpha.0 → 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/backend/AxiosRequestBackend.d.ts +9 -5
- package/cjs/backend/AxiosRequestBackend.js +28 -10
- package/cjs/backend/FetchRequestBackend.d.ts +9 -5
- package/cjs/backend/FetchRequestBackend.js +18 -11
- package/cjs/backend/WebSocketRequest.d.ts +11 -0
- package/cjs/backend/WebSocketRequest.js +36 -0
- package/esm/backend/AxiosRequestBackend.d.ts +9 -5
- package/esm/backend/AxiosRequestBackend.js +28 -10
- package/esm/backend/FetchRequestBackend.d.ts +9 -5
- package/esm/backend/FetchRequestBackend.js +18 -11
- package/esm/backend/WebSocketRequest.d.ts +11 -0
- package/esm/backend/WebSocketRequest.js +31 -0
- package/package.json +1 -1
|
@@ -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,22 +86,22 @@ 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) {
|
|
85
103
|
if (context.responseType === "websocket") {
|
|
86
|
-
|
|
104
|
+
return (0, WebSocketRequest_1.makeWebSocketRequest)(context, this.webSocketConstructor);
|
|
87
105
|
}
|
|
88
106
|
const { requestConfig } = context;
|
|
89
107
|
const url = context.requestUrl;
|
|
@@ -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") {
|
|
@@ -83,12 +94,6 @@ class FetchRequestBackend {
|
|
|
83
94
|
else if (responseType === "stream") {
|
|
84
95
|
data = response.body;
|
|
85
96
|
}
|
|
86
|
-
else if (responseType === "websocket") {
|
|
87
|
-
data = response.webSocket;
|
|
88
|
-
if (!data) {
|
|
89
|
-
throw new Error("[api-def] WebSocket upgrade response did not include a webSocket");
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
97
|
else {
|
|
93
98
|
data = await response.text();
|
|
94
99
|
}
|
|
@@ -116,13 +121,15 @@ class FetchRequestBackend {
|
|
|
116
121
|
const abortSignal = abortController ? abortController.signal : undefined;
|
|
117
122
|
let softAbort = false;
|
|
118
123
|
let responded = false;
|
|
124
|
+
if (context.responseType === "websocket") {
|
|
125
|
+
responded = true;
|
|
126
|
+
return (0, WebSocketRequest_1.makeWebSocketRequest)(context, this.webSocketConstructor);
|
|
127
|
+
}
|
|
119
128
|
const body = context.getParsedBody();
|
|
120
129
|
const bodyJsonify = body !== null && typeof body === "object" && !Utils.isFormDataLike(body) && !(body instanceof URLSearchParams);
|
|
121
130
|
const headers = Utils.assign({
|
|
122
131
|
// logic from axios
|
|
123
132
|
"Content-Type": bodyJsonify ? "application/json;charset=utf-8" : undefined,
|
|
124
|
-
Connection: context.responseType === "websocket" ? "Upgrade" : undefined,
|
|
125
|
-
Upgrade: context.responseType === "websocket" ? "websocket" : undefined,
|
|
126
133
|
}, requestConfig.headers);
|
|
127
134
|
const parsedHeaders = Object.keys(headers).reduce((parsedHeaders, key) => {
|
|
128
135
|
const value = headers[key];
|
|
@@ -157,7 +164,7 @@ class FetchRequestBackend {
|
|
|
157
164
|
}
|
|
158
165
|
const promise = this.fetch(request).then((response) => {
|
|
159
166
|
responded = true;
|
|
160
|
-
if (!response.ok
|
|
167
|
+
if (!response.ok) {
|
|
161
168
|
const error = new FetchError("Fetch failed");
|
|
162
169
|
error.response = response;
|
|
163
170
|
throw error;
|
|
@@ -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;
|
|
@@ -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,22 +82,22 @@ 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) {
|
|
81
99
|
if (context.responseType === "websocket") {
|
|
82
|
-
|
|
100
|
+
return makeWebSocketRequest(context, this.webSocketConstructor);
|
|
83
101
|
}
|
|
84
102
|
const { requestConfig } = context;
|
|
85
103
|
const url = context.requestUrl;
|
|
@@ -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") {
|
|
@@ -48,12 +59,6 @@ export default class FetchRequestBackend {
|
|
|
48
59
|
else if (responseType === "stream") {
|
|
49
60
|
data = response.body;
|
|
50
61
|
}
|
|
51
|
-
else if (responseType === "websocket") {
|
|
52
|
-
data = response.webSocket;
|
|
53
|
-
if (!data) {
|
|
54
|
-
throw new Error("[api-def] WebSocket upgrade response did not include a webSocket");
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
62
|
else {
|
|
58
63
|
data = await response.text();
|
|
59
64
|
}
|
|
@@ -81,13 +86,15 @@ export default class FetchRequestBackend {
|
|
|
81
86
|
const abortSignal = abortController ? abortController.signal : undefined;
|
|
82
87
|
let softAbort = false;
|
|
83
88
|
let responded = false;
|
|
89
|
+
if (context.responseType === "websocket") {
|
|
90
|
+
responded = true;
|
|
91
|
+
return makeWebSocketRequest(context, this.webSocketConstructor);
|
|
92
|
+
}
|
|
84
93
|
const body = context.getParsedBody();
|
|
85
94
|
const bodyJsonify = body !== null && typeof body === "object" && !Utils.isFormDataLike(body) && !(body instanceof URLSearchParams);
|
|
86
95
|
const headers = Utils.assign({
|
|
87
96
|
// logic from axios
|
|
88
97
|
"Content-Type": bodyJsonify ? "application/json;charset=utf-8" : undefined,
|
|
89
|
-
Connection: context.responseType === "websocket" ? "Upgrade" : undefined,
|
|
90
|
-
Upgrade: context.responseType === "websocket" ? "websocket" : undefined,
|
|
91
98
|
}, requestConfig.headers);
|
|
92
99
|
const parsedHeaders = Object.keys(headers).reduce((parsedHeaders, key) => {
|
|
93
100
|
const value = headers[key];
|
|
@@ -122,7 +129,7 @@ export default class FetchRequestBackend {
|
|
|
122
129
|
}
|
|
123
130
|
const promise = this.fetch(request).then((response) => {
|
|
124
131
|
responded = true;
|
|
125
|
-
if (!response.ok
|
|
132
|
+
if (!response.ok) {
|
|
126
133
|
const error = new FetchError("Fetch failed");
|
|
127
134
|
error.response = response;
|
|
128
135
|
throw error;
|
|
@@ -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
|
+
};
|