@synnaxlabs/freighter 0.1.0
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/LICENSE +21 -0
- package/README.md +3 -0
- package/build/main/index.d.ts +7 -0
- package/build/main/index.js +27 -0
- package/build/main/lib/caseconv.d.ts +2 -0
- package/build/main/lib/caseconv.js +14 -0
- package/build/main/lib/encoder.d.ts +60 -0
- package/build/main/lib/encoder.js +61 -0
- package/build/main/lib/encoder.spec.d.ts +1 -0
- package/build/main/lib/encoder.spec.js +26 -0
- package/build/main/lib/errors.d.ts +77 -0
- package/build/main/lib/errors.js +171 -0
- package/build/main/lib/errors.spec.d.ts +1 -0
- package/build/main/lib/errors.spec.js +76 -0
- package/build/main/lib/http.d.ts +46 -0
- package/build/main/lib/http.js +99 -0
- package/build/main/lib/http.spec.d.ts +1 -0
- package/build/main/lib/http.spec.js +49 -0
- package/build/main/lib/runtime.d.ts +5 -0
- package/build/main/lib/runtime.js +22 -0
- package/build/main/lib/stream.d.ts +75 -0
- package/build/main/lib/stream.js +3 -0
- package/build/main/lib/unary.d.ts +14 -0
- package/build/main/lib/unary.js +3 -0
- package/build/main/lib/url.d.ts +37 -0
- package/build/main/lib/url.js +57 -0
- package/build/main/lib/url.spec.d.ts +1 -0
- package/build/main/lib/url.spec.js +36 -0
- package/build/main/lib/websocket.d.ts +21 -0
- package/build/main/lib/websocket.js +154 -0
- package/build/main/lib/ws.spec.d.ts +1 -0
- package/build/main/lib/ws.spec.js +73 -0
- package/build/module/index.d.ts +7 -0
- package/build/module/index.js +6 -0
- package/build/module/lib/caseconv.d.ts +2 -0
- package/build/module/lib/caseconv.js +9 -0
- package/build/module/lib/encoder.d.ts +60 -0
- package/build/module/lib/encoder.js +51 -0
- package/build/module/lib/encoder.spec.d.ts +1 -0
- package/build/module/lib/encoder.spec.js +21 -0
- package/build/module/lib/errors.d.ts +77 -0
- package/build/module/lib/errors.js +160 -0
- package/build/module/lib/errors.spec.d.ts +1 -0
- package/build/module/lib/errors.spec.js +71 -0
- package/build/module/lib/http.d.ts +46 -0
- package/build/module/lib/http.js +94 -0
- package/build/module/lib/http.spec.d.ts +1 -0
- package/build/module/lib/http.spec.js +44 -0
- package/build/module/lib/runtime.d.ts +5 -0
- package/build/module/lib/runtime.js +19 -0
- package/build/module/lib/stream.d.ts +75 -0
- package/build/module/lib/stream.js +2 -0
- package/build/module/lib/unary.d.ts +14 -0
- package/build/module/lib/unary.js +2 -0
- package/build/module/lib/url.d.ts +37 -0
- package/build/module/lib/url.js +61 -0
- package/build/module/lib/url.spec.d.ts +1 -0
- package/build/module/lib/url.spec.js +31 -0
- package/build/module/lib/websocket.d.ts +21 -0
- package/build/module/lib/websocket.js +160 -0
- package/build/module/lib/ws.spec.d.ts +1 -0
- package/build/module/lib/ws.spec.js +69 -0
- package/package.json +118 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { decodeError, ErrorPayloadSchema } from './errors';
|
|
3
|
+
/**
|
|
4
|
+
* HTTPClientFactory provides a POST and GET implementation of the Unary protocol.
|
|
5
|
+
*
|
|
6
|
+
* @param url - The base URL of the API.
|
|
7
|
+
* @param encoder - The encoder/decoder to use for the request/response.
|
|
8
|
+
*/
|
|
9
|
+
export class HTTPClientFactory {
|
|
10
|
+
endpoint;
|
|
11
|
+
encoder;
|
|
12
|
+
constructor(endpoint, encoder) {
|
|
13
|
+
this.endpoint = endpoint;
|
|
14
|
+
this.encoder = encoder;
|
|
15
|
+
}
|
|
16
|
+
getClient() {
|
|
17
|
+
return new GETClient(this.endpoint, this.encoder);
|
|
18
|
+
}
|
|
19
|
+
postClient() {
|
|
20
|
+
return new POSTClient(this.endpoint, this.encoder);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
class Core {
|
|
24
|
+
endpoint;
|
|
25
|
+
encoder;
|
|
26
|
+
static ERROR_ENCODING_HEADER_KEY = 'Error-Encoding';
|
|
27
|
+
static ERROR_ENCODING_HEADER_VALUE = 'freighter';
|
|
28
|
+
static CONTENT_TYPE_HEADER_KEY = 'Content-Type';
|
|
29
|
+
constructor(endpoint, encoder) {
|
|
30
|
+
this.endpoint = endpoint.replace({ protocol: 'http' });
|
|
31
|
+
this.encoder = encoder;
|
|
32
|
+
}
|
|
33
|
+
get headers() {
|
|
34
|
+
return {
|
|
35
|
+
[Core.CONTENT_TYPE_HEADER_KEY]: this.encoder.contentType,
|
|
36
|
+
[Core.ERROR_ENCODING_HEADER_KEY]: Core.ERROR_ENCODING_HEADER_VALUE,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
requestConfig() {
|
|
40
|
+
return {
|
|
41
|
+
headers: this.headers,
|
|
42
|
+
responseType: 'arraybuffer',
|
|
43
|
+
withCredentials: false,
|
|
44
|
+
validateStatus: () => true,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
async execute(request, resSchema) {
|
|
48
|
+
const response = await axios.request(request);
|
|
49
|
+
if (response.status < 200 || response.status >= 300) {
|
|
50
|
+
try {
|
|
51
|
+
const err = this.encoder.decode(response.data, ErrorPayloadSchema);
|
|
52
|
+
return [undefined, decodeError(err)];
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return [undefined, new Error(response.data)];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
const data = this.encoder.decode(response.data, resSchema);
|
|
59
|
+
return [data, undefined];
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Implementation of the UnaryClient protocol backed by HTTP GET requests. It
|
|
64
|
+
* should not be instantiated directly, but through the HTTPClientFactory.
|
|
65
|
+
*/
|
|
66
|
+
export class GETClient extends Core {
|
|
67
|
+
async send(target, req, resSchema) {
|
|
68
|
+
const queryString = buildQueryString(req);
|
|
69
|
+
const request = this.requestConfig();
|
|
70
|
+
request.method = 'GET';
|
|
71
|
+
request.url = this.endpoint.child(target).stringify() + '?' + queryString;
|
|
72
|
+
return await this.execute(request, resSchema);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Implementation of the UnaryClient protocol backed by HTTP POST requests. It
|
|
77
|
+
* should not be instantiated directly, but through the HTTPClientFactory.
|
|
78
|
+
*/
|
|
79
|
+
export class POSTClient extends Core {
|
|
80
|
+
async send(target, req, resSchema) {
|
|
81
|
+
const url = this.endpoint.child(target).stringify();
|
|
82
|
+
const request = this.requestConfig();
|
|
83
|
+
request.method = 'POST';
|
|
84
|
+
request.url = url;
|
|
85
|
+
request.data = this.encoder.encode(req);
|
|
86
|
+
return await this.execute(request, resSchema);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const buildQueryString = (request) => {
|
|
90
|
+
return Object.keys(request)
|
|
91
|
+
.map((key) => `${key}=${request[key]}`)
|
|
92
|
+
.join('&');
|
|
93
|
+
};
|
|
94
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvaHR0cC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQTZCLE1BQU0sT0FBTyxDQUFDO0FBSWxELE9BQU8sRUFBRSxXQUFXLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFJM0Q7Ozs7O0dBS0c7QUFDSCxNQUFNLE9BQU8saUJBQWlCO0lBQzVCLFFBQVEsQ0FBTTtJQUNkLE9BQU8sQ0FBaUI7SUFFeEIsWUFBWSxRQUFhLEVBQUUsT0FBdUI7UUFDaEQsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUM7UUFDekIsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVELFNBQVM7UUFDUCxPQUFPLElBQUksU0FBUyxDQUFDLElBQUksQ0FBQyxRQUFRLEVBQUUsSUFBSSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUFFRCxVQUFVO1FBQ1IsT0FBTyxJQUFJLFVBQVUsQ0FBQyxJQUFJLENBQUMsUUFBUSxFQUFFLElBQUksQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUNyRCxDQUFDO0NBQ0Y7QUFFRCxNQUFNLElBQUk7SUFDUixRQUFRLENBQU07SUFDZCxPQUFPLENBQWlCO0lBRWhCLE1BQU0sQ0FBQyx5QkFBeUIsR0FBRyxnQkFBZ0IsQ0FBQztJQUNwRCxNQUFNLENBQUMsMkJBQTJCLEdBQUcsV0FBVyxDQUFDO0lBQ2pELE1BQU0sQ0FBQyx1QkFBdUIsR0FBRyxjQUFjLENBQUM7SUFFeEQsWUFBWSxRQUFhLEVBQUUsT0FBdUI7UUFDaEQsSUFBSSxDQUFDLFFBQVEsR0FBRyxRQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDdkQsSUFBSSxDQUFDLE9BQU8sR0FBRyxPQUFPLENBQUM7SUFDekIsQ0FBQztJQUVELElBQUksT0FBTztRQUNULE9BQU87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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import { JSONEncoderDecoder } from './encoder';
|
|
4
|
+
import { HTTPClientFactory } from './http';
|
|
5
|
+
import URL from './url';
|
|
6
|
+
const ENDPOINT = new URL({
|
|
7
|
+
host: '127.0.0.1',
|
|
8
|
+
port: 8080,
|
|
9
|
+
pathPrefix: 'http',
|
|
10
|
+
});
|
|
11
|
+
const factory = new HTTPClientFactory(ENDPOINT, new JSONEncoderDecoder());
|
|
12
|
+
const MessageSchema = z.object({
|
|
13
|
+
id: z.number().optional(),
|
|
14
|
+
message: z.string().optional(),
|
|
15
|
+
});
|
|
16
|
+
const getClient = factory.getClient();
|
|
17
|
+
const postClient = factory.postClient();
|
|
18
|
+
test('[http] - post echo', async (t) => {
|
|
19
|
+
const [response, error] = await postClient.send('/echo', {
|
|
20
|
+
id: 1,
|
|
21
|
+
message: 'hello',
|
|
22
|
+
}, MessageSchema);
|
|
23
|
+
t.is(error, undefined);
|
|
24
|
+
t.deepEqual(response, { id: 2, message: 'hello' });
|
|
25
|
+
});
|
|
26
|
+
test('[http] - get echo', async (t) => {
|
|
27
|
+
const [response, error] = await getClient.send('/echo', {
|
|
28
|
+
id: 1,
|
|
29
|
+
message: 'hello',
|
|
30
|
+
}, MessageSchema);
|
|
31
|
+
t.is(error, undefined);
|
|
32
|
+
t.deepEqual(response, { id: 2, message: 'hello' });
|
|
33
|
+
});
|
|
34
|
+
test('[http] - get not found', async (t) => {
|
|
35
|
+
const [response, error] = await getClient.send('/not-found', {}, MessageSchema);
|
|
36
|
+
t.is(error?.message, 'Cannot GET /http/not-found/');
|
|
37
|
+
t.is(response, undefined);
|
|
38
|
+
});
|
|
39
|
+
test('[http] - post not found', async (t) => {
|
|
40
|
+
const [response, error] = await postClient.send('/not-found', {}, MessageSchema);
|
|
41
|
+
t.is(error?.message, 'Cannot POST /http/not-found/');
|
|
42
|
+
t.is(response, undefined);
|
|
43
|
+
});
|
|
44
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaHR0cC5zcGVjLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi9odHRwLnNwZWMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxJQUFJLE1BQU0sS0FBSyxDQUFDO0FBQ3ZCLE9BQU8sRUFBRSxDQUFDLEVBQUUsTUFBTSxLQUFLLENBQUM7QUFFeEIsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sV0FBVyxDQUFDO0FBQy9DLE9BQU8sRUFBRSxpQkFBaUIsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQUMzQyxPQUFPLEdBQUcsTUFBTSxPQUFPLENBQUM7QUFFeEIsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLENBQUM7SUFDdkIsSUFBSSxFQUFFLFdBQVc7SUFDakIsSUFBSSxFQUFFLElBQUk7SUFDVixVQUFVLEVBQUUsTUFBTTtDQUNuQixDQUFDLENBQUM7QUFFSCxNQUFNLE9BQU8sR0FBRyxJQUFJLGlCQUFpQixDQUFDLFFBQVEsRUFBRSxJQUFJLGtCQUFrQixFQUFFLENBQUMsQ0FBQztBQUUxRSxNQUFNLGFBQWEsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzdCLEVBQUUsRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0lBQ3pCLE9BQU8sRUFBRSxDQUFDLENBQUMsTUFBTSxFQUFFLENBQUMsUUFBUSxFQUFFO0NBQy9CLENBQUMsQ0FBQztBQUlILE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxTQUFTLEVBQUUsQ0FBQztBQUN0QyxNQUFNLFVBQVUsR0FBRyxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUM7QUFFeEMsSUFBSSxDQUFDLG9CQUFvQixFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtJQUNyQyxNQUFNLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxHQUFHLE1BQU0sVUFBVSxDQUFDLElBQUksQ0FDN0MsT0FBTyxFQUNQO1FBQ0UsRUFBRSxFQUFFLENBQUM7UUFDTCxPQUFPLEVBQUUsT0FBTztLQUNqQixFQUNELGFBQWEsQ0FDZCxDQUFDO0lBQ0YsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDdkIsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO0FBQ3JELENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLG1CQUFtQixFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtJQUNwQyxNQUFNLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxHQUFHLE1BQU0sU0FBUyxDQUFDLElBQUksQ0FDNUMsT0FBTyxFQUNQO1FBQ0UsRUFBRSxFQUFFLENBQUM7UUFDTCxPQUFPLEVBQUUsT0FBTztLQUNqQixFQUNELGFBQWEsQ0FDZCxDQUFDO0lBQ0YsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDdkIsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxRQUFRLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDO0FBQ3JELENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLHdCQUF3QixFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtJQUN6QyxNQUFNLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxHQUFHLE1BQU0sU0FBUyxDQUFDLElBQUksQ0FDNUMsWUFBWSxFQUNaLEVBQUUsRUFDRixhQUFhLENBQ2QsQ0FBQztJQUNGLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSw2QkFBNkIsQ0FBQyxDQUFDO0lBQ3BELENBQUMsQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQzVCLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLHlCQUF5QixFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsRUFBRTtJQUMxQyxNQUFNLENBQUMsUUFBUSxFQUFFLEtBQUssQ0FBQyxHQUFHLE1BQU0sVUFBVSxDQUFDLElBQUksQ0FDN0MsWUFBWSxFQUNaLEVBQUUsRUFDRixhQUFhLENBQ2QsQ0FBQztJQUNGLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxFQUFFLE9BQU8sRUFBRSw4QkFBOEIsQ0FBQyxDQUFDO0lBQ3JELENBQUMsQ0FBQyxFQUFFLENBQUMsUUFBUSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQzVCLENBQUMsQ0FBQyxDQUFDIn0=
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export var Runtime;
|
|
2
|
+
(function (Runtime) {
|
|
3
|
+
Runtime["Browser"] = "browser";
|
|
4
|
+
Runtime["Node"] = "node";
|
|
5
|
+
})(Runtime || (Runtime = {}));
|
|
6
|
+
const detectRuntime = () => {
|
|
7
|
+
if (typeof process !== 'undefined' &&
|
|
8
|
+
process.versions != null &&
|
|
9
|
+
process.versions.node != null) {
|
|
10
|
+
return Runtime.Node;
|
|
11
|
+
}
|
|
12
|
+
if (typeof window !== 'undefined' && typeof window.document !== 'undefined') {
|
|
13
|
+
return Runtime.Browser;
|
|
14
|
+
}
|
|
15
|
+
console.warn('Freighter unable to safely detect runtime, assuming browser');
|
|
16
|
+
return Runtime.Browser;
|
|
17
|
+
};
|
|
18
|
+
export const RUNTIME = detectRuntime();
|
|
19
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVudGltZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvcnVudGltZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxNQUFNLENBQU4sSUFBWSxPQUdYO0FBSEQsV0FBWSxPQUFPO0lBQ2pCLDhCQUFtQixDQUFBO0lBQ25CLHdCQUFhLENBQUE7QUFDZixDQUFDLEVBSFcsT0FBTyxLQUFQLE9BQU8sUUFHbEI7QUFFRCxNQUFNLGFBQWEsR0FBRyxHQUFZLEVBQUU7SUFDbEMsSUFDRSxPQUFPLE9BQU8sS0FBSyxXQUFXO1FBQzlCLE9BQU8sQ0FBQyxRQUFRLElBQUksSUFBSTtRQUN4QixPQUFPLENBQUMsUUFBUSxDQUFDLElBQUksSUFBSSxJQUFJLEVBQzdCO1FBQ0EsT0FBTyxPQUFPLENBQUMsSUFBSSxDQUFDO0tBQ3JCO0lBRUQsSUFBSSxPQUFPLE1BQU0sS0FBSyxXQUFXLElBQUksT0FBTyxNQUFNLENBQUMsUUFBUSxLQUFLLFdBQVcsRUFBRTtRQUMzRSxPQUFPLE9BQU8sQ0FBQyxPQUFPLENBQUM7S0FDeEI7SUFFRCxPQUFPLENBQUMsSUFBSSxDQUFDLDZEQUE2RCxDQUFDLENBQUM7SUFDNUUsT0FBTyxPQUFPLENBQUMsT0FBTyxDQUFDO0FBQ3pCLENBQUMsQ0FBQztBQUVGLE1BQU0sQ0FBQyxNQUFNLE9BQU8sR0FBRyxhQUFhLEVBQUUsQ0FBQyJ9
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { ZodSchema } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Interface for an entity that receives a stream of responses.
|
|
4
|
+
*/
|
|
5
|
+
export interface StreamReceiver<RS> {
|
|
6
|
+
/**
|
|
7
|
+
* Receives a response from the stream. It's not safe to call receive
|
|
8
|
+
* concurrently.
|
|
9
|
+
*
|
|
10
|
+
* @returns freighter.EOF: if the server closed the stream nominally.
|
|
11
|
+
* @returns Error: if the server closed the stream abnormally,
|
|
12
|
+
* returns the error the server returned.
|
|
13
|
+
* @raises Error: if the transport fails.
|
|
14
|
+
*/
|
|
15
|
+
receive(): Promise<[RS | undefined, Error | undefined]>;
|
|
16
|
+
/**
|
|
17
|
+
* @returns true if the stream has received a response
|
|
18
|
+
*/
|
|
19
|
+
received(): boolean;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Interface for an entity that sends a stream of requests.
|
|
23
|
+
*/
|
|
24
|
+
export interface StreamSender<RQ> {
|
|
25
|
+
/**
|
|
26
|
+
* Sends a request to the stream. It is not safe to call send concurrently
|
|
27
|
+
* with closeSend or send.
|
|
28
|
+
|
|
29
|
+
* @param req - the request to send.
|
|
30
|
+
* @returns freighter.EOF: if the server closed the stream. The caller
|
|
31
|
+
* can discover the error returned by the server by calling receive().
|
|
32
|
+
* @returns undefined: if the message was sent successfully.
|
|
33
|
+
* @raises freighter.StreamClosed: if the client called close_send()
|
|
34
|
+
* @raises Error: if the transport fails.
|
|
35
|
+
*/
|
|
36
|
+
send(req: RQ): Error | undefined;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extension of the StreamSender interface that allows the client to close the sending
|
|
40
|
+
* direction of the stream when finished issuing requrest.
|
|
41
|
+
*/
|
|
42
|
+
export interface StreamSenderCloser<RQ> extends StreamSender<RQ> {
|
|
43
|
+
/**
|
|
44
|
+
* Lets the server know no more messages will be sent. If the client attempts
|
|
45
|
+
* to call send() after calling closeSend(), a freighter.StreamClosed
|
|
46
|
+
* exception will be raised. close_send is idempotent. If the server has
|
|
47
|
+
* already closed the stream, close_send will do nothing.
|
|
48
|
+
|
|
49
|
+
* After calling close_send, the client is responsible for calling receive()
|
|
50
|
+
* to successfully receive the server's acknowledgement.
|
|
51
|
+
*/
|
|
52
|
+
closeSend(): void;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Interface for a bidirectional stream between a client and a server.
|
|
56
|
+
*/
|
|
57
|
+
export interface Stream<RQ, RS> extends StreamSenderCloser<RQ>, StreamReceiver<RS> {
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Interface for a bidirectional stream between a client and a server.
|
|
61
|
+
*/
|
|
62
|
+
export interface StreamClient {
|
|
63
|
+
/**
|
|
64
|
+
* Dials the target and returns a stream that can be used to issue requests
|
|
65
|
+
* and receive responses
|
|
66
|
+
*
|
|
67
|
+
* @param target - The target to dial. In some implementations, this may be
|
|
68
|
+
* an endpoint path, or in others, a complete hostname or URL.
|
|
69
|
+
* @param reqSchema - The schema for the request type. This is used to
|
|
70
|
+
* validate the request before sending it.
|
|
71
|
+
* @param resSchema - The schema for the response type. This is used to
|
|
72
|
+
* validate the response before returning it.
|
|
73
|
+
*/
|
|
74
|
+
stream<RQ, RS>(target: string, reqSchema: ZodSchema<RQ>, resSchema: ZodSchema<RS>): Promise<Stream<RQ, RS>>;
|
|
75
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ZodSchema } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* An interface for an entity that implements a simple request-response
|
|
4
|
+
* transport between two entities.
|
|
5
|
+
*/
|
|
6
|
+
export interface UnaryClient {
|
|
7
|
+
/**
|
|
8
|
+
* Sends a request to the target server and waits until a response is received.
|
|
9
|
+
* @param target - The target server to send the request to.
|
|
10
|
+
* @param req - The request to send.
|
|
11
|
+
* @param resSchema - The schema to validate the response against.
|
|
12
|
+
*/
|
|
13
|
+
send<RQ, RS>(target: string, req: RQ, resSchema: ZodSchema<RS>): Promise<[RS | undefined, Error | undefined]>;
|
|
14
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
declare type URLProps = {
|
|
2
|
+
host: string;
|
|
3
|
+
port: number;
|
|
4
|
+
protocol?: string;
|
|
5
|
+
pathPrefix?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* URL is a simple class for building and extending URLs.
|
|
9
|
+
*/
|
|
10
|
+
export default class URL {
|
|
11
|
+
protocol: string;
|
|
12
|
+
host: string;
|
|
13
|
+
port: number;
|
|
14
|
+
path: string;
|
|
15
|
+
/**
|
|
16
|
+
* @param host - The hostname or IP address of the server.
|
|
17
|
+
* @param port - The port number of the server.
|
|
18
|
+
* @param protocol - The protocol to use for all requests. Defaults to "".
|
|
19
|
+
* @param pathPrefix - A path prefix to use for all requests. Defaults to "".
|
|
20
|
+
*/
|
|
21
|
+
constructor({ host, port, protocol, pathPrefix }: URLProps);
|
|
22
|
+
/**
|
|
23
|
+
* Replaces creates a new URL with the specified properties replaced.
|
|
24
|
+
* @param props - The properties to replace.
|
|
25
|
+
* @returns a new URL.
|
|
26
|
+
*/
|
|
27
|
+
replace(props: Partial<URLProps>): URL;
|
|
28
|
+
/**
|
|
29
|
+
* Creates a new url with the given path appended to the current path.
|
|
30
|
+
* @param path - the path to append to the URL.
|
|
31
|
+
* @returns a new URL.
|
|
32
|
+
*/
|
|
33
|
+
child(path: string): URL;
|
|
34
|
+
/** @returns a string representation of the url */
|
|
35
|
+
stringify(): string;
|
|
36
|
+
}
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* URL is a simple class for building and extending URLs.
|
|
3
|
+
*/
|
|
4
|
+
export default class URL {
|
|
5
|
+
protocol;
|
|
6
|
+
host;
|
|
7
|
+
port;
|
|
8
|
+
path;
|
|
9
|
+
/**
|
|
10
|
+
* @param host - The hostname or IP address of the server.
|
|
11
|
+
* @param port - The port number of the server.
|
|
12
|
+
* @param protocol - The protocol to use for all requests. Defaults to "".
|
|
13
|
+
* @param pathPrefix - A path prefix to use for all requests. Defaults to "".
|
|
14
|
+
*/
|
|
15
|
+
constructor({ host, port, protocol = '', pathPrefix = '' }) {
|
|
16
|
+
this.protocol = protocol;
|
|
17
|
+
this.host = host;
|
|
18
|
+
this.port = port;
|
|
19
|
+
this.path = formatPath(pathPrefix);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Replaces creates a new URL with the specified properties replaced.
|
|
23
|
+
* @param props - The properties to replace.
|
|
24
|
+
* @returns a new URL.
|
|
25
|
+
*/
|
|
26
|
+
replace(props) {
|
|
27
|
+
return new URL({
|
|
28
|
+
host: props.host || this.host,
|
|
29
|
+
port: props.port || this.port,
|
|
30
|
+
protocol: props.protocol || this.protocol,
|
|
31
|
+
pathPrefix: props.pathPrefix || this.path,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a new url with the given path appended to the current path.
|
|
36
|
+
* @param path - the path to append to the URL.
|
|
37
|
+
* @returns a new URL.
|
|
38
|
+
*/
|
|
39
|
+
child(path) {
|
|
40
|
+
return new URL({
|
|
41
|
+
...this,
|
|
42
|
+
pathPrefix: joinPaths(this.path, path),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
/** @returns a string representation of the url */
|
|
46
|
+
stringify() {
|
|
47
|
+
return `${this.protocol}://${this.host}:${this.port}/${this.path}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// joinPath joins the two paths, ensuring there is a single slash between them.
|
|
51
|
+
const joinPaths = (...paths) => {
|
|
52
|
+
return paths.map(formatPath).join('');
|
|
53
|
+
};
|
|
54
|
+
const formatPath = (path) => {
|
|
55
|
+
if (!path.endsWith('/'))
|
|
56
|
+
path += '/';
|
|
57
|
+
if (path.startsWith('/'))
|
|
58
|
+
path = path.slice(1);
|
|
59
|
+
return path;
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXJsLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi91cmwudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBT0E7O0dBRUc7QUFDSCxNQUFNLENBQUMsT0FBTyxPQUFPLEdBQUc7SUFDdEIsUUFBUSxDQUFTO0lBQ2pCLElBQUksQ0FBUztJQUNiLElBQUksQ0FBUztJQUNiLElBQUksQ0FBUztJQUViOzs7OztPQUtHO0lBQ0gsWUFBWSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsUUFBUSxHQUFHLEVBQUUsRUFBRSxVQUFVLEdBQUcsRUFBRSxFQUFZO1FBQ2xFLElBQUksQ0FBQyxRQUFRLEdBQUcsUUFBUSxDQUFDO1FBQ3pCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO1FBQ2pCLElBQUksQ0FBQyxJQUFJLEdBQUcsVUFBVSxDQUFDLFVBQVUsQ0FBQyxDQUFDO0lBQ3JDLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsT0FBTyxDQUFDLEtBQXdCO1FBQzlCLE9BQU8sSUFBSSxHQUFHLENBQUM7WUFDYixJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSTtZQUM3QixJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSTtZQUM3QixRQUFRLEVBQUUsS0FBSyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUMsUUFBUTtZQUN6QyxVQUFVLEVBQUUsS0FBSyxDQUFDLFVBQVUsSUFBSSxJQUFJLENBQUMsSUFBSTtTQUMxQyxDQUFDLENBQUM7SUFDTCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILEtBQUssQ0FBQyxJQUFZO1FBQ2hCLE9BQU8sSUFBSSxHQUFHLENBQUM7WUFDYixHQUFHLElBQUk7WUFDUCxVQUFVLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDO1NBQ3ZDLENBQUMsQ0FBQztJQUNMLENBQUM7SUFFRCxrREFBa0Q7SUFDbEQsU0FBUztRQUNQLE9BQU8sR0FBRyxJQUFJLENBQUMsUUFBUSxNQUFNLElBQUksQ0FBQyxJQUFJLElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxFQUFFLENBQUM7SUFDckUsQ0FBQztDQUNGO0FBRUQsK0VBQStFO0FBQy9FLE1BQU0sU0FBUyxHQUFHLENBQUMsR0FBRyxLQUFlLEVBQVUsRUFBRTtJQUMvQyxPQUFPLEtBQUssQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDO0FBQ3hDLENBQUMsQ0FBQztBQUVGLE1BQU0sVUFBVSxHQUFHLENBQUMsSUFBWSxFQUFVLEVBQUU7SUFDMUMsSUFBSSxDQUFDLElBQUksQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDO1FBQUUsSUFBSSxJQUFJLEdBQUcsQ0FBQztJQUNyQyxJQUFJLElBQUksQ0FBQyxVQUFVLENBQUMsR0FBRyxDQUFDO1FBQUUsSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFDL0MsT0FBTyxJQUFJLENBQUM7QUFDZCxDQUFDLENBQUMifQ==
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import URL from './url';
|
|
3
|
+
test('URL - child', (t) => {
|
|
4
|
+
const endpoint = new URL({
|
|
5
|
+
host: 'localhost',
|
|
6
|
+
port: 8080,
|
|
7
|
+
protocol: 'http',
|
|
8
|
+
pathPrefix: 'api',
|
|
9
|
+
});
|
|
10
|
+
t.is(endpoint.child('test').stringify(), 'http://localhost:8080/api/test/');
|
|
11
|
+
});
|
|
12
|
+
test('URL - child with trailing slash', (t) => {
|
|
13
|
+
const endpoint = new URL({
|
|
14
|
+
host: 'localhost',
|
|
15
|
+
port: 8080,
|
|
16
|
+
protocol: 'http',
|
|
17
|
+
pathPrefix: 'api',
|
|
18
|
+
});
|
|
19
|
+
const child = endpoint.child('test/');
|
|
20
|
+
t.is(child.stringify(), 'http://localhost:8080/api/test/');
|
|
21
|
+
});
|
|
22
|
+
test('URL - replacing protocol', (t) => {
|
|
23
|
+
const endpoint = new URL({
|
|
24
|
+
host: 'localhost',
|
|
25
|
+
port: 8080,
|
|
26
|
+
protocol: 'http',
|
|
27
|
+
pathPrefix: 'api',
|
|
28
|
+
});
|
|
29
|
+
t.is(endpoint.child('test').replace({ protocol: 'https' }).stringify(), 'https://localhost:8080/api/test/');
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXJsLnNwZWMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL3VybC5zcGVjLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sSUFBSSxNQUFNLEtBQUssQ0FBQztBQUV2QixPQUFPLEdBQUcsTUFBTSxPQUFPLENBQUM7QUFFeEIsSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFDLENBQUMsRUFBRSxFQUFFO0lBQ3hCLE1BQU0sUUFBUSxHQUFHLElBQUksR0FBRyxDQUFDO1FBQ3ZCLElBQUksRUFBRSxXQUFXO1FBQ2pCLElBQUksRUFBRSxJQUFJO1FBQ1YsUUFBUSxFQUFFLE1BQU07UUFDaEIsVUFBVSxFQUFFLEtBQUs7S0FDbEIsQ0FBQyxDQUFDO0lBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLFNBQVMsRUFBRSxFQUFFLGlDQUFpQyxDQUFDLENBQUM7QUFDOUUsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsaUNBQWlDLEVBQUUsQ0FBQyxDQUFDLEVBQUUsRUFBRTtJQUM1QyxNQUFNLFFBQVEsR0FBRyxJQUFJLEdBQUcsQ0FBQztRQUN2QixJQUFJLEVBQUUsV0FBVztRQUNqQixJQUFJLEVBQUUsSUFBSTtRQUNWLFFBQVEsRUFBRSxNQUFNO1FBQ2hCLFVBQVUsRUFBRSxLQUFLO0tBQ2xCLENBQUMsQ0FBQztJQUNILE1BQU0sS0FBSyxHQUFHLFFBQVEsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDdEMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLEVBQUUsaUNBQWlDLENBQUMsQ0FBQztBQUM3RCxDQUFDLENBQUMsQ0FBQztBQUVILElBQUksQ0FBQywwQkFBMEIsRUFBRSxDQUFDLENBQUMsRUFBRSxFQUFFO0lBQ3JDLE1BQU0sUUFBUSxHQUFHLElBQUksR0FBRyxDQUFDO1FBQ3ZCLElBQUksRUFBRSxXQUFXO1FBQ2pCLElBQUksRUFBRSxJQUFJO1FBQ1YsUUFBUSxFQUFFLE1BQU07UUFDaEIsVUFBVSxFQUFFLEtBQUs7S0FDbEIsQ0FBQyxDQUFDO0lBQ0gsQ0FBQyxDQUFDLEVBQUUsQ0FDRixRQUFRLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLFFBQVEsRUFBRSxPQUFPLEVBQUUsQ0FBQyxDQUFDLFNBQVMsRUFBRSxFQUNqRSxrQ0FBa0MsQ0FDbkMsQ0FBQztBQUNKLENBQUMsQ0FBQyxDQUFDIn0=
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ZodSchema } from 'zod';
|
|
2
|
+
import { EncoderDecoder } from './encoder';
|
|
3
|
+
import { Stream, StreamClient } from './stream';
|
|
4
|
+
import URL from './url';
|
|
5
|
+
/**
|
|
6
|
+
* WebSocketClient is an implementation of StreamClient that is backed by
|
|
7
|
+
* websockets.
|
|
8
|
+
*/
|
|
9
|
+
export declare class WebSocketClient implements StreamClient {
|
|
10
|
+
url: URL;
|
|
11
|
+
encoder: EncoderDecoder;
|
|
12
|
+
static readonly MESSAGE_TYPE = "arraybuffer";
|
|
13
|
+
/**
|
|
14
|
+
* @param encoder - The encoder to use for encoding messages and decoding
|
|
15
|
+
* responses.
|
|
16
|
+
* @param baseURL - A base url to use as a prefix for all requests.
|
|
17
|
+
*/
|
|
18
|
+
constructor(encoder: EncoderDecoder, baseURL: URL);
|
|
19
|
+
/** Implements the StreamClient interface. */
|
|
20
|
+
stream<RQ, RS>(target: string, reqSchema: ZodSchema<RQ>, resSchema: ZodSchema<RS>): Promise<Stream<RQ, RS>>;
|
|
21
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { decodeError, EOF, ErrorPayloadSchema, StreamClosed } from './errors';
|
|
3
|
+
import { Runtime, RUNTIME } from './runtime';
|
|
4
|
+
const resolveWebsocketProvider = () => {
|
|
5
|
+
if (RUNTIME == Runtime.Node)
|
|
6
|
+
return require('ws');
|
|
7
|
+
return WebSocket;
|
|
8
|
+
};
|
|
9
|
+
var MessageType;
|
|
10
|
+
(function (MessageType) {
|
|
11
|
+
MessageType["Data"] = "data";
|
|
12
|
+
MessageType["Close"] = "close";
|
|
13
|
+
})(MessageType || (MessageType = {}));
|
|
14
|
+
const MessageSchema = z.object({
|
|
15
|
+
type: z.nativeEnum(MessageType),
|
|
16
|
+
payload: z.unknown().optional(),
|
|
17
|
+
error: z.optional(ErrorPayloadSchema),
|
|
18
|
+
});
|
|
19
|
+
var CloseCode;
|
|
20
|
+
(function (CloseCode) {
|
|
21
|
+
CloseCode[CloseCode["Normal"] = 1000] = "Normal";
|
|
22
|
+
CloseCode[CloseCode["GoingAway"] = 1001] = "GoingAway";
|
|
23
|
+
})(CloseCode || (CloseCode = {}));
|
|
24
|
+
/**
|
|
25
|
+
* WebSocketStream is an implementation of Stream that is backed by a websocket.
|
|
26
|
+
*/
|
|
27
|
+
class WebSocketStream {
|
|
28
|
+
encoder;
|
|
29
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
30
|
+
// @ts-ignore
|
|
31
|
+
reqSchema;
|
|
32
|
+
resSchema;
|
|
33
|
+
ws;
|
|
34
|
+
server_closed;
|
|
35
|
+
send_closed;
|
|
36
|
+
receiveDataQueue = [];
|
|
37
|
+
receiveCallbacksQueue = [];
|
|
38
|
+
constructor(ws, encoder, reqSchema, resSchema) {
|
|
39
|
+
this.encoder = encoder;
|
|
40
|
+
this.reqSchema = reqSchema;
|
|
41
|
+
this.resSchema = resSchema;
|
|
42
|
+
this.ws = ws;
|
|
43
|
+
this.send_closed = false;
|
|
44
|
+
this.listenForMessages();
|
|
45
|
+
}
|
|
46
|
+
/** Implements the Stream protocol */
|
|
47
|
+
send(req) {
|
|
48
|
+
if (this.server_closed)
|
|
49
|
+
return new EOF();
|
|
50
|
+
if (this.send_closed)
|
|
51
|
+
throw new StreamClosed();
|
|
52
|
+
this.ws.send(this.encoder.encode({
|
|
53
|
+
type: MessageType.Data,
|
|
54
|
+
payload: req,
|
|
55
|
+
error: undefined,
|
|
56
|
+
}));
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Implements the Stream protocol */
|
|
60
|
+
async receive() {
|
|
61
|
+
if (this.server_closed)
|
|
62
|
+
return [undefined, this.server_closed];
|
|
63
|
+
const msg = await this.receiveMsg();
|
|
64
|
+
if (msg.type == MessageType.Close) {
|
|
65
|
+
if (!msg.error)
|
|
66
|
+
throw new Error('Message error must be defined');
|
|
67
|
+
this.server_closed = decodeError(msg.error);
|
|
68
|
+
return [undefined, this.server_closed];
|
|
69
|
+
}
|
|
70
|
+
return [this.resSchema.parse(msg.payload), undefined];
|
|
71
|
+
}
|
|
72
|
+
/** Implements the Stream protocol */
|
|
73
|
+
received() {
|
|
74
|
+
return this.receiveDataQueue.length !== 0;
|
|
75
|
+
}
|
|
76
|
+
/** Implements the Stream protocol */
|
|
77
|
+
closeSend() {
|
|
78
|
+
if (this.send_closed || this.server_closed) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
const msg = { type: MessageType.Close };
|
|
82
|
+
try {
|
|
83
|
+
this.ws.send(this.encoder.encode(msg));
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
this.send_closed = true;
|
|
87
|
+
}
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
async receiveMsg() {
|
|
91
|
+
if (this.receiveDataQueue.length !== 0) {
|
|
92
|
+
const msg = this.receiveDataQueue.shift();
|
|
93
|
+
if (msg)
|
|
94
|
+
return msg;
|
|
95
|
+
throw new Error('unexpected undefined message');
|
|
96
|
+
}
|
|
97
|
+
return new Promise((resolve, reject) => {
|
|
98
|
+
this.receiveCallbacksQueue.push({ resolve, reject });
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
listenForMessages() {
|
|
102
|
+
this.ws.onmessage = (ev) => {
|
|
103
|
+
const msg = this.encoder.decode(ev.data, MessageSchema);
|
|
104
|
+
if (this.receiveCallbacksQueue.length > 0) {
|
|
105
|
+
const callback = this.receiveCallbacksQueue.shift();
|
|
106
|
+
if (callback)
|
|
107
|
+
callback.resolve(msg);
|
|
108
|
+
else
|
|
109
|
+
throw new Error('Unexpected empty callback queue');
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
this.receiveDataQueue.push(msg);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
this.ws.onclose = (ev) => {
|
|
116
|
+
if ([CloseCode.Normal, CloseCode.GoingAway].includes(ev.code)) {
|
|
117
|
+
this.server_closed = new EOF();
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
this.server_closed = new StreamClosed();
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* WebSocketClient is an implementation of StreamClient that is backed by
|
|
127
|
+
* websockets.
|
|
128
|
+
*/
|
|
129
|
+
export class WebSocketClient {
|
|
130
|
+
url;
|
|
131
|
+
encoder;
|
|
132
|
+
static MESSAGE_TYPE = 'arraybuffer';
|
|
133
|
+
/**
|
|
134
|
+
* @param encoder - The encoder to use for encoding messages and decoding
|
|
135
|
+
* responses.
|
|
136
|
+
* @param baseURL - A base url to use as a prefix for all requests.
|
|
137
|
+
*/
|
|
138
|
+
constructor(encoder, baseURL) {
|
|
139
|
+
this.url = baseURL.replace({ protocol: 'ws' });
|
|
140
|
+
this.encoder = encoder;
|
|
141
|
+
}
|
|
142
|
+
/** Implements the StreamClient interface. */
|
|
143
|
+
async stream(target, reqSchema, resSchema) {
|
|
144
|
+
const ResolvedWebSocket = resolveWebsocketProvider();
|
|
145
|
+
const url = this.url.child(target).stringify() +
|
|
146
|
+
'?contentType=' +
|
|
147
|
+
this.encoder.contentType;
|
|
148
|
+
const ws = new ResolvedWebSocket(url);
|
|
149
|
+
ws.binaryType = WebSocketClient.MESSAGE_TYPE;
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
ws.onopen = () => {
|
|
152
|
+
resolve(new WebSocketStream(ws, this.encoder, reqSchema, resSchema));
|
|
153
|
+
};
|
|
154
|
+
ws.onerror = (ev) => {
|
|
155
|
+
reject(ev);
|
|
156
|
+
};
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2Vic29ja2V0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL2xpYi93ZWJzb2NrZXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLENBQUMsRUFBYSxNQUFNLEtBQUssQ0FBQztBQUduQyxPQUFPLEVBQUUsV0FBVyxFQUFFLEdBQUcsRUFBRSxrQkFBa0IsRUFBRSxZQUFZLEVBQUUsTUFBTSxVQUFVLENBQUM7QUFDOUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxXQUFXLENBQUM7QUFJN0MsTUFBTSx3QkFBd0IsR0FBRyxHQUFxQixFQUFFO0lBQ3RELElBQUksT0FBTyxJQUFJLE9BQU8sQ0FBQyxJQUFJO1FBQUUsT0FBTyxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDbEQsT0FBTyxTQUFTLENBQUM7QUFDbkIsQ0FBQyxDQUFDO0FBRUYsSUFBSyxXQUdKO0FBSEQsV0FBSyxXQUFXO0lBQ2QsNEJBQWEsQ0FBQTtJQUNiLDhCQUFlLENBQUE7QUFDakIsQ0FBQyxFQUhJLFdBQVcsS0FBWCxXQUFXLFFBR2Y7QUFFRCxNQUFNLGFBQWEsR0FBRyxDQUFDLENBQUMsTUFBTSxDQUFDO0lBQzdCLElBQUksRUFBRSxDQUFDLENBQUMsVUFBVSxDQUFDLFdBQVcsQ0FBQztJQUMvQixPQUFPLEVBQUUsQ0FBQyxDQUFDLE9BQU8sRUFBRSxDQUFDLFFBQVEsRUFBRTtJQUMvQixLQUFLLEVBQUUsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxrQkFBa0IsQ0FBQztDQUN0QyxDQUFDLENBQUM7QUFJSCxJQUFLLFNBR0o7QUFIRCxXQUFLLFNBQVM7SUFDWixnREFBYSxDQUFBO0lBQ2Isc0RBQWdCLENBQUE7QUFDbEIsQ0FBQyxFQUhJLFNBQVMsS0FBVCxTQUFTLFFBR2I7QUFFRDs7R0FFRztBQUNILE1BQU0sZUFBZTtJQUNYLE9BQU8sQ0FBaUI7SUFDaEMsNkRBQTZEO0lBQzdELGFBQWE7SUFDTCxTQUFTLENB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
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|