@synnaxlabs/freighter 0.42.3 → 0.44.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/.vscode/settings.json +2 -2
- package/dist/errors.d.ts +5 -5
- package/dist/errors.d.ts.map +1 -1
- package/dist/freighter.cjs +4 -11
- package/dist/freighter.js +1693 -2237
- package/dist/http.d.ts +1 -2
- package/dist/http.d.ts.map +1 -1
- package/dist/stream.d.ts +7 -7
- package/dist/stream.d.ts.map +1 -1
- package/dist/unary.d.ts +3 -3
- package/dist/unary.d.ts.map +1 -1
- package/dist/websocket.d.ts +1 -1
- package/dist/websocket.d.ts.map +1 -1
- package/eslint.config.js +1 -1
- package/package.json +15 -16
- package/src/alamos.ts +3 -3
- package/src/errors.ts +5 -5
- package/src/http.spec.ts +22 -0
- package/src/http.ts +15 -28
- package/src/stream.ts +7 -7
- package/src/unary.ts +10 -13
- package/src/websocket.ts +8 -20
- package/tsconfig.json +1 -1
- package/README.md +0 -3
package/dist/http.d.ts
CHANGED
|
@@ -13,9 +13,8 @@ export declare const CONTENT_TYPE_HEADER_KEY = "Content-Type";
|
|
|
13
13
|
export declare class HTTPClient extends MiddlewareCollector implements UnaryClient {
|
|
14
14
|
endpoint: URL;
|
|
15
15
|
encoder: binary.Codec;
|
|
16
|
-
fetch: typeof fetch;
|
|
17
16
|
constructor(endpoint: URL, encoder: binary.Codec, secure?: boolean);
|
|
18
17
|
get headers(): Record<string, string>;
|
|
19
|
-
send<RQ extends z.
|
|
18
|
+
send<RQ extends z.ZodType, RS extends z.ZodType = RQ>(target: string, req: z.input<RQ> | z.infer<RQ>, reqSchema: RQ, resSchema: RS): Promise<[z.infer<RS>, null] | [null, Error]>;
|
|
20
19
|
}
|
|
21
20
|
//# sourceMappingURL=http.d.ts.map
|
package/dist/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,MAAM,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,MAAM,EAAU,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAG7B,OAAO,EAAgB,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE3C,eAAO,MAAM,uBAAuB,iBAAiB,CAAC;AAUtD;;;;;;GAMG;AACH,qBAAa,UAAW,SAAQ,mBAAoB,YAAW,WAAW;IACxE,QAAQ,EAAE,GAAG,CAAC;IACd,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;gBAEV,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,GAAE,OAAe;IAazE,IAAI,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAIpC;IAEK,IAAI,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,OAAO,GAAG,EAAE,EACxD,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAC9B,SAAS,EAAE,EAAE,EACb,SAAS,EAAE,EAAE,GACZ,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;CAuDhD"}
|
package/dist/stream.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { Transport } from './transport';
|
|
|
3
3
|
/**
|
|
4
4
|
* Interface for an entity that receives a stream of responses.
|
|
5
5
|
*/
|
|
6
|
-
export interface StreamReceiver<RS extends z.
|
|
6
|
+
export interface StreamReceiver<RS extends z.ZodType> {
|
|
7
7
|
/**
|
|
8
8
|
* Receives a response from the stream. It's not safe to call receive
|
|
9
9
|
* concurrently.
|
|
@@ -13,7 +13,7 @@ export interface StreamReceiver<RS extends z.ZodTypeAny> {
|
|
|
13
13
|
* returns the error the server returned.
|
|
14
14
|
* @raises Error: if the transport fails.
|
|
15
15
|
*/
|
|
16
|
-
receive: () => Promise<[z.
|
|
16
|
+
receive: () => Promise<[z.infer<RS>, null] | [null, Error]>;
|
|
17
17
|
/**
|
|
18
18
|
* @returns true if the stream has received a response
|
|
19
19
|
*/
|
|
@@ -22,7 +22,7 @@ export interface StreamReceiver<RS extends z.ZodTypeAny> {
|
|
|
22
22
|
/**
|
|
23
23
|
* Interface for an entity that sends a stream of requests.
|
|
24
24
|
*/
|
|
25
|
-
export interface StreamSender<RQ extends z.
|
|
25
|
+
export interface StreamSender<RQ extends z.ZodType> {
|
|
26
26
|
/**
|
|
27
27
|
* Sends a request to the stream. It is not safe to call send concurrently
|
|
28
28
|
* with closeSend or send.
|
|
@@ -34,13 +34,13 @@ export interface StreamSender<RQ extends z.ZodTypeAny> {
|
|
|
34
34
|
* @raises freighter.StreamClosed: if the client called close_send()
|
|
35
35
|
* @raises Error: if the transport fails.
|
|
36
36
|
*/
|
|
37
|
-
send: (req: z.input<RQ> | z.
|
|
37
|
+
send: (req: z.input<RQ> | z.infer<RQ>) => Error | null;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
40
|
* Extension of the StreamSender interface that allows the client to close the sending
|
|
41
41
|
* direction of the stream when finished issuing requrest.
|
|
42
42
|
*/
|
|
43
|
-
export interface StreamSenderCloser<RQ extends z.
|
|
43
|
+
export interface StreamSenderCloser<RQ extends z.ZodType> extends StreamSender<RQ> {
|
|
44
44
|
/**
|
|
45
45
|
* Lets the server know no more messages will be sent. If the client attempts
|
|
46
46
|
* to call send() after calling closeSend(), a freighter.StreamClosed
|
|
@@ -55,7 +55,7 @@ export interface StreamSenderCloser<RQ extends z.ZodTypeAny> extends StreamSende
|
|
|
55
55
|
/**
|
|
56
56
|
* Interface for a bidirectional stream between a client and a server.
|
|
57
57
|
*/
|
|
58
|
-
export interface Stream<RQ extends z.
|
|
58
|
+
export interface Stream<RQ extends z.ZodType, RS extends z.ZodType = RQ> extends StreamSenderCloser<RQ>, StreamReceiver<RS> {
|
|
59
59
|
}
|
|
60
60
|
/**
|
|
61
61
|
* Interface for a bidirectional stream between a client and a server.
|
|
@@ -72,6 +72,6 @@ export interface StreamClient extends Transport {
|
|
|
72
72
|
* @param resSchema - The schema for the response type. This is used to
|
|
73
73
|
* validate the response before returning it.
|
|
74
74
|
*/
|
|
75
|
-
stream: <RQ extends z.
|
|
75
|
+
stream: <RQ extends z.ZodType, RS extends z.ZodType = RQ>(target: string, reqSchema: RQ, resSchema: RS) => Promise<Stream<RQ, RS>>;
|
|
76
76
|
}
|
|
77
77
|
//# sourceMappingURL=stream.d.ts.map
|
package/dist/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,EAAE,SAAS,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,cAAc,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO;IAClD;;;;;;;;OAQG;IACH,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IAE5D;;OAEG;IACH,QAAQ,EAAE,MAAM,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO;IAChD;;;;;;;;;;MAUE;IACF,IAAI,EAAE,CAAC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,KAAK,GAAG,IAAI,CAAC;CACxD;AAED;;;GAGG;AACH,MAAM,WAAW,kBAAkB,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,CAAE,SAAQ,YAAY,CAAC,EAAE,CAAC;IAChF;;;;;;;;OAQG;IACH,SAAS,EAAE,MAAM,IAAI,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,OAAO,GAAG,EAAE,CACrE,SAAQ,kBAAkB,CAAC,EAAE,CAAC,EAC5B,cAAc,CAAC,EAAE,CAAC;CAAG;AAEzB;;GAEG;AACH,MAAM,WAAW,YAAa,SAAQ,SAAS;IAC7C;;;;;;;;;;OAUG;IACH,MAAM,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,OAAO,GAAG,EAAE,EACtD,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,EAAE,EACb,SAAS,EAAE,EAAE,KACV,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC;CAC9B"}
|
package/dist/unary.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { breaker
|
|
1
|
+
import { breaker } from '@synnaxlabs/x';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { Transport } from './transport';
|
|
4
4
|
/**
|
|
@@ -12,8 +12,8 @@ export interface UnaryClient extends Transport {
|
|
|
12
12
|
* @param req - The request to send.
|
|
13
13
|
* @param resSchema - The schema to validate the response against.
|
|
14
14
|
*/
|
|
15
|
-
send: <RQ extends z.
|
|
15
|
+
send: <RQ extends z.ZodType, RS extends z.ZodType = RQ>(target: string, req: z.input<RQ> | z.infer<RQ>, reqSchema: RQ, resSchema: RS) => Promise<[z.infer<RS>, null] | [null, Error]>;
|
|
16
16
|
}
|
|
17
17
|
export declare const unaryWithBreaker: (base: UnaryClient, cfg: breaker.Config) => UnaryClient;
|
|
18
|
-
export declare const sendRequired: <RQ extends z.
|
|
18
|
+
export declare const sendRequired: <RQ extends z.ZodType, RS extends z.ZodType = RQ>(client: UnaryClient, target: string, req: z.input<RQ> | z.infer<RQ>, reqSchema: RQ, resSchema: RS) => Promise<z.infer<RS>>;
|
|
19
19
|
//# sourceMappingURL=unary.d.ts.map
|
package/dist/unary.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"unary.d.ts","sourceRoot":"","sources":["../src/unary.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"unary.d.ts","sourceRoot":"","sources":["../src/unary.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAI7B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C;;;;;OAKG;IACH,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,OAAO,GAAG,EAAE,EACpD,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAC9B,SAAS,EAAE,EAAE,EACb,SAAS,EAAE,EAAE,KACV,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;CACnD;AAED,eAAO,MAAM,gBAAgB,GAC3B,MAAM,WAAW,EACjB,KAAK,OAAO,CAAC,MAAM,KAClB,WA4BF,CAAC;AAEF,eAAO,MAAM,YAAY,GAAU,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,OAAO,GAAG,EAAE,EAChF,QAAQ,WAAW,EACnB,QAAQ,MAAM,EACd,KAAK,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,EAC9B,WAAW,EAAE,EACb,WAAW,EAAE,KACZ,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAIrB,CAAC"}
|
package/dist/websocket.d.ts
CHANGED
|
@@ -25,7 +25,7 @@ export declare class WebSocketClient extends MiddlewareCollector implements Stre
|
|
|
25
25
|
constructor(baseEndpoint: URL, encoder: binary.Codec, secure?: boolean);
|
|
26
26
|
withCodec(codec: binary.Codec): WebSocketClient;
|
|
27
27
|
/** Implements the StreamClient interface. */
|
|
28
|
-
stream<RQ extends z.
|
|
28
|
+
stream<RQ extends z.ZodType, RS extends z.ZodType = RQ>(target: string, reqSchema: RQ, resSchema: RS): Promise<Stream<RQ, RS>>;
|
|
29
29
|
private buildURL;
|
|
30
30
|
private wrapSocket;
|
|
31
31
|
}
|
package/dist/websocket.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AASA,OAAO,
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,MAAM,EAAoB,MAAM,EAAE,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,OAAO,EAAgB,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,YAAY,EAAE,MAAM,UAAU,CAAC;AAQ1D,MAAM,MAAM,gBAAgB,CAAC,CAAC,GAAG,OAAO,IAAI;IAC1C,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,CAAC,CAAC;CACb,CAAC;AA+GF,eAAO,MAAM,yBAAyB,iBAAiB,CAAC;AAIxD;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,mBAAoB,YAAW,YAAY;IAC9E,OAAO,EAAE,GAAG,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAEhB,MAAM,CAAC,QAAQ,CAAC,YAAY,iBAAiB;IAE7C;;;;OAIG;gBACS,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,UAAQ;IAOpE,SAAS,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,GAAG,eAAe;IAM/C,6CAA6C;IACvC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC,OAAO,GAAG,EAAE,EAC1D,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,EAAE,EACb,SAAS,EAAE,EAAE,GACZ,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAkB1B,OAAO,CAAC,QAAQ;YAWF,UAAU;CAsBzB"}
|
package/eslint.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synnaxlabs/freighter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.44.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "a modular transport abstraction",
|
|
6
6
|
"repository": "https://github.com/synnaxlabs/synnax/tree/main/freighter/ts",
|
|
@@ -14,33 +14,32 @@
|
|
|
14
14
|
"control systems"
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
"@synnaxlabs/alamos": "0.42.3",
|
|
21
|
-
"@synnaxlabs/x": "0.42.3"
|
|
17
|
+
"zod": "^4.0.2",
|
|
18
|
+
"@synnaxlabs/alamos": "^0.44.0",
|
|
19
|
+
"@synnaxlabs/x": "^0.44.0"
|
|
22
20
|
},
|
|
23
21
|
"devDependencies": {
|
|
24
|
-
"@types/node": "^
|
|
25
|
-
"@vitest/coverage-v8": "^3.
|
|
26
|
-
"eslint": "^9.
|
|
22
|
+
"@types/node": "^24.0.12",
|
|
23
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
24
|
+
"eslint": "^9.30.1",
|
|
27
25
|
"madge": "^8.0.0",
|
|
28
26
|
"typescript": "^5.8.3",
|
|
29
|
-
"vite": "^
|
|
30
|
-
"vitest": "^3.
|
|
31
|
-
"@synnaxlabs/
|
|
32
|
-
"
|
|
33
|
-
"
|
|
27
|
+
"vite": "^7.0.4",
|
|
28
|
+
"vitest": "^3.2.4",
|
|
29
|
+
"@synnaxlabs/tsconfig": "^0.43.0",
|
|
30
|
+
"@synnaxlabs/vite-plugin": "^0.43.0",
|
|
31
|
+
"eslint-config-synnaxlabs": "^0.43.0"
|
|
34
32
|
},
|
|
35
33
|
"main": "dist/freighter.cjs",
|
|
36
34
|
"module": "dist/freighter.js",
|
|
37
35
|
"types": "dist/index.d.ts",
|
|
38
36
|
"scripts": {
|
|
39
37
|
"build": "tsc --noEmit && vite build",
|
|
38
|
+
"check-types": "tsc --noEmit",
|
|
40
39
|
"watch": "tsc --noEmit && vite build --watch",
|
|
41
40
|
"test": "vitest",
|
|
42
|
-
"lint": "eslint
|
|
43
|
-
"fix": "eslint --
|
|
41
|
+
"lint": "eslint",
|
|
42
|
+
"fix": "eslint --fix",
|
|
44
43
|
"madge": "madge --circular src"
|
|
45
44
|
}
|
|
46
45
|
}
|
package/src/alamos.ts
CHANGED
|
@@ -15,8 +15,8 @@ export const middleware =
|
|
|
15
15
|
(instrumentation: Instrumentation): Middleware =>
|
|
16
16
|
async (context, next) => {
|
|
17
17
|
if (context.role === "client") instrumentation.T.propagate(context.params);
|
|
18
|
-
|
|
19
|
-
const [res, exc] =
|
|
18
|
+
|
|
19
|
+
const [res, exc] = await instrumentation.T.trace(
|
|
20
20
|
context.target,
|
|
21
21
|
"debug",
|
|
22
22
|
async (span): Promise<[Context, Error | null]> => {
|
|
@@ -24,7 +24,7 @@ export const middleware =
|
|
|
24
24
|
if (err != null) span.recordError(err);
|
|
25
25
|
return [ctx, err];
|
|
26
26
|
},
|
|
27
|
-
)
|
|
27
|
+
);
|
|
28
28
|
log(context, instrumentation, exc);
|
|
29
29
|
return [res, exc];
|
|
30
30
|
};
|
package/src/errors.ts
CHANGED
|
@@ -10,12 +10,12 @@
|
|
|
10
10
|
import { errors, URL } from "@synnaxlabs/x";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
13
|
+
* Base class for all freighter-specific errors
|
|
14
14
|
*/
|
|
15
15
|
export class FreighterError extends errors.createTyped("freighter") {}
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
|
-
*
|
|
18
|
+
* Error thrown when reaching the end of a file or stream
|
|
19
19
|
*/
|
|
20
20
|
export class EOF extends FreighterError.sub("eof") {
|
|
21
21
|
constructor() {
|
|
@@ -24,7 +24,7 @@ export class EOF extends FreighterError.sub("eof") {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
*
|
|
27
|
+
* Error thrown when attempting to operate on a closed stream
|
|
28
28
|
*/
|
|
29
29
|
export class StreamClosed extends FreighterError.sub("stream_closed") {
|
|
30
30
|
constructor() {
|
|
@@ -33,14 +33,14 @@ export class StreamClosed extends FreighterError.sub("stream_closed") {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Arguments for constructing an Unreachable error
|
|
37
37
|
*/
|
|
38
38
|
export interface UnreachableArgs {
|
|
39
39
|
message?: string;
|
|
40
40
|
url?: URL;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
/**
|
|
43
|
+
/** Thrown when a network target is unreachable. */
|
|
44
44
|
export class Unreachable extends FreighterError.sub("unreachable") {
|
|
45
45
|
url: URL;
|
|
46
46
|
|
package/src/http.spec.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { binary, URL } from "@synnaxlabs/x";
|
|
|
11
11
|
import { describe, expect, test } from "vitest";
|
|
12
12
|
import { z } from "zod";
|
|
13
13
|
|
|
14
|
+
import { Unreachable } from "@/errors";
|
|
14
15
|
import { HTTPClient } from "@/http";
|
|
15
16
|
|
|
16
17
|
const ENDPOINT = new URL({
|
|
@@ -67,4 +68,25 @@ describe("http", () => {
|
|
|
67
68
|
expect(error).toBeNull();
|
|
68
69
|
expect(response?.message).toEqual("");
|
|
69
70
|
});
|
|
71
|
+
|
|
72
|
+
test("unreachable", async () => {
|
|
73
|
+
const c = new HTTPClient(
|
|
74
|
+
new URL({
|
|
75
|
+
host: "127.0.0.1",
|
|
76
|
+
protocol: "http",
|
|
77
|
+
port: 9999,
|
|
78
|
+
pathPrefix: "unary",
|
|
79
|
+
}),
|
|
80
|
+
new binary.JSONCodec(),
|
|
81
|
+
);
|
|
82
|
+
const [response, error] = await c.send<typeof messageZ>(
|
|
83
|
+
"/unreachable",
|
|
84
|
+
{},
|
|
85
|
+
messageZ,
|
|
86
|
+
messageZ,
|
|
87
|
+
);
|
|
88
|
+
expect(error).toBeInstanceOf(Unreachable);
|
|
89
|
+
expect(error?.message).toEqual("Unreachable");
|
|
90
|
+
expect(response).toBeNull();
|
|
91
|
+
});
|
|
70
92
|
});
|
package/src/http.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { type binary, errors,
|
|
10
|
+
import { type binary, errors, type URL } from "@synnaxlabs/x";
|
|
11
11
|
import { type z } from "zod";
|
|
12
12
|
|
|
13
13
|
import { Unreachable } from "@/errors";
|
|
@@ -16,21 +16,11 @@ import { type UnaryClient } from "@/unary";
|
|
|
16
16
|
|
|
17
17
|
export const CONTENT_TYPE_HEADER_KEY = "Content-Type";
|
|
18
18
|
|
|
19
|
-
const resolveFetchAPI = (protocol: "http" | "https"): typeof fetch => {
|
|
20
|
-
if (runtime.RUNTIME !== "node") return fetch;
|
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
22
|
-
const _fetch: typeof fetch = require("node-fetch");
|
|
23
|
-
if (protocol === "http") return _fetch;
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
|
-
const https = require("https");
|
|
26
|
-
const agent = new https.Agent({ rejectUnauthorized: false });
|
|
27
|
-
// @ts-expect-error - TS doesn't know about qhis option
|
|
28
|
-
return async (info, init) => await _fetch(info, { ...init, agent });
|
|
29
|
-
};
|
|
30
|
-
|
|
31
19
|
const shouldCastToUnreachable = (err: Error): boolean =>
|
|
32
|
-
|
|
33
|
-
err.
|
|
20
|
+
typeof err.cause === "object" &&
|
|
21
|
+
err.cause !== null &&
|
|
22
|
+
"code" in err.cause &&
|
|
23
|
+
err.cause.code === "ECONNREFUSED";
|
|
34
24
|
|
|
35
25
|
const HTTP_STATUS_BAD_REQUEST = 400;
|
|
36
26
|
|
|
@@ -44,13 +34,11 @@ const HTTP_STATUS_BAD_REQUEST = 400;
|
|
|
44
34
|
export class HTTPClient extends MiddlewareCollector implements UnaryClient {
|
|
45
35
|
endpoint: URL;
|
|
46
36
|
encoder: binary.Codec;
|
|
47
|
-
fetch: typeof fetch;
|
|
48
37
|
|
|
49
38
|
constructor(endpoint: URL, encoder: binary.Codec, secure: boolean = false) {
|
|
50
39
|
super();
|
|
51
40
|
this.endpoint = endpoint.replace({ protocol: secure ? "https" : "http" });
|
|
52
41
|
this.encoder = encoder;
|
|
53
|
-
this.fetch = resolveFetchAPI(this.endpoint.protocol as "http" | "https");
|
|
54
42
|
|
|
55
43
|
return new Proxy(this, {
|
|
56
44
|
get: (target, prop, receiver) => {
|
|
@@ -66,14 +54,14 @@ export class HTTPClient extends MiddlewareCollector implements UnaryClient {
|
|
|
66
54
|
};
|
|
67
55
|
}
|
|
68
56
|
|
|
69
|
-
async send<RQ extends z.
|
|
57
|
+
async send<RQ extends z.ZodType, RS extends z.ZodType = RQ>(
|
|
70
58
|
target: string,
|
|
71
|
-
req: z.input<RQ> | z.
|
|
59
|
+
req: z.input<RQ> | z.infer<RQ>,
|
|
72
60
|
reqSchema: RQ,
|
|
73
61
|
resSchema: RS,
|
|
74
|
-
): Promise<[z.
|
|
62
|
+
): Promise<[z.infer<RS>, null] | [null, Error]> {
|
|
75
63
|
req = reqSchema?.parse(req);
|
|
76
|
-
let res: z.
|
|
64
|
+
let res: z.infer<RS> | null = null;
|
|
77
65
|
const url = this.endpoint.child(target);
|
|
78
66
|
const request: RequestInit = {};
|
|
79
67
|
request.method = "POST";
|
|
@@ -93,14 +81,12 @@ export class HTTPClient extends MiddlewareCollector implements UnaryClient {
|
|
|
93
81
|
};
|
|
94
82
|
let httpRes: Response;
|
|
95
83
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if (shouldCastToUnreachable(err)) err = new Unreachable({ url });
|
|
101
|
-
return [outCtx, err];
|
|
84
|
+
httpRes = await fetch(ctx.target, request);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
if (!(e instanceof Error)) throw e;
|
|
87
|
+
return [outCtx, shouldCastToUnreachable(e) ? new Unreachable({ url }) : e];
|
|
102
88
|
}
|
|
103
|
-
const data =
|
|
89
|
+
const data = await httpRes.arrayBuffer();
|
|
104
90
|
if (httpRes?.ok) {
|
|
105
91
|
if (resSchema != null) res = this.encoder.decode<RS>(data, resSchema);
|
|
106
92
|
return [outCtx, null];
|
|
@@ -125,6 +111,7 @@ export class HTTPClient extends MiddlewareCollector implements UnaryClient {
|
|
|
125
111
|
);
|
|
126
112
|
|
|
127
113
|
if (err != null) return [null, err];
|
|
114
|
+
if (res == null) throw new Error("Response must be defined");
|
|
128
115
|
return [res, null];
|
|
129
116
|
}
|
|
130
117
|
}
|
package/src/stream.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { type Transport } from "@/transport";
|
|
|
14
14
|
/**
|
|
15
15
|
* Interface for an entity that receives a stream of responses.
|
|
16
16
|
*/
|
|
17
|
-
export interface StreamReceiver<RS extends z.
|
|
17
|
+
export interface StreamReceiver<RS extends z.ZodType> {
|
|
18
18
|
/**
|
|
19
19
|
* Receives a response from the stream. It's not safe to call receive
|
|
20
20
|
* concurrently.
|
|
@@ -24,7 +24,7 @@ export interface StreamReceiver<RS extends z.ZodTypeAny> {
|
|
|
24
24
|
* returns the error the server returned.
|
|
25
25
|
* @raises Error: if the transport fails.
|
|
26
26
|
*/
|
|
27
|
-
receive: () => Promise<[z.
|
|
27
|
+
receive: () => Promise<[z.infer<RS>, null] | [null, Error]>;
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* @returns true if the stream has received a response
|
|
@@ -35,7 +35,7 @@ export interface StreamReceiver<RS extends z.ZodTypeAny> {
|
|
|
35
35
|
/**
|
|
36
36
|
* Interface for an entity that sends a stream of requests.
|
|
37
37
|
*/
|
|
38
|
-
export interface StreamSender<RQ extends z.
|
|
38
|
+
export interface StreamSender<RQ extends z.ZodType> {
|
|
39
39
|
/**
|
|
40
40
|
* Sends a request to the stream. It is not safe to call send concurrently
|
|
41
41
|
* with closeSend or send.
|
|
@@ -47,14 +47,14 @@ export interface StreamSender<RQ extends z.ZodTypeAny> {
|
|
|
47
47
|
* @raises freighter.StreamClosed: if the client called close_send()
|
|
48
48
|
* @raises Error: if the transport fails.
|
|
49
49
|
*/
|
|
50
|
-
send: (req: z.input<RQ> | z.
|
|
50
|
+
send: (req: z.input<RQ> | z.infer<RQ>) => Error | null;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
* Extension of the StreamSender interface that allows the client to close the sending
|
|
55
55
|
* direction of the stream when finished issuing requrest.
|
|
56
56
|
*/
|
|
57
|
-
export interface StreamSenderCloser<RQ extends z.
|
|
57
|
+
export interface StreamSenderCloser<RQ extends z.ZodType> extends StreamSender<RQ> {
|
|
58
58
|
/**
|
|
59
59
|
* Lets the server know no more messages will be sent. If the client attempts
|
|
60
60
|
* to call send() after calling closeSend(), a freighter.StreamClosed
|
|
@@ -70,7 +70,7 @@ export interface StreamSenderCloser<RQ extends z.ZodTypeAny> extends StreamSende
|
|
|
70
70
|
/**
|
|
71
71
|
* Interface for a bidirectional stream between a client and a server.
|
|
72
72
|
*/
|
|
73
|
-
export interface Stream<RQ extends z.
|
|
73
|
+
export interface Stream<RQ extends z.ZodType, RS extends z.ZodType = RQ>
|
|
74
74
|
extends StreamSenderCloser<RQ>,
|
|
75
75
|
StreamReceiver<RS> {}
|
|
76
76
|
|
|
@@ -89,7 +89,7 @@ export interface StreamClient extends Transport {
|
|
|
89
89
|
* @param resSchema - The schema for the response type. This is used to
|
|
90
90
|
* validate the response before returning it.
|
|
91
91
|
*/
|
|
92
|
-
stream: <RQ extends z.
|
|
92
|
+
stream: <RQ extends z.ZodType, RS extends z.ZodType = RQ>(
|
|
93
93
|
target: string,
|
|
94
94
|
reqSchema: RQ,
|
|
95
95
|
resSchema: RS,
|
package/src/unary.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import { breaker
|
|
10
|
+
import { breaker } from "@synnaxlabs/x";
|
|
11
11
|
import { type z } from "zod";
|
|
12
12
|
|
|
13
13
|
import { Unreachable } from "@/errors";
|
|
@@ -25,12 +25,12 @@ export interface UnaryClient extends Transport {
|
|
|
25
25
|
* @param req - The request to send.
|
|
26
26
|
* @param resSchema - The schema to validate the response against.
|
|
27
27
|
*/
|
|
28
|
-
send: <RQ extends z.
|
|
28
|
+
send: <RQ extends z.ZodType, RS extends z.ZodType = RQ>(
|
|
29
29
|
target: string,
|
|
30
|
-
req: z.input<RQ> | z.
|
|
30
|
+
req: z.input<RQ> | z.infer<RQ>,
|
|
31
31
|
reqSchema: RQ,
|
|
32
32
|
resSchema: RS,
|
|
33
|
-
) => Promise<
|
|
33
|
+
) => Promise<[z.infer<RS>, null] | [null, Error]>;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
export const unaryWithBreaker = (
|
|
@@ -48,12 +48,12 @@ export const unaryWithBreaker = (
|
|
|
48
48
|
this.wrapped.use(...mw);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
async send<RQ extends z.
|
|
51
|
+
async send<RQ extends z.ZodType, RS extends z.ZodType = RQ>(
|
|
52
52
|
target: string,
|
|
53
|
-
req: z.input<RQ> | z.
|
|
53
|
+
req: z.input<RQ> | z.infer<RQ>,
|
|
54
54
|
reqSchema: RQ,
|
|
55
55
|
resSchema: RS,
|
|
56
|
-
): Promise<
|
|
56
|
+
): Promise<[z.infer<RS>, null] | [null, Error]> {
|
|
57
57
|
const brk = new breaker.Breaker(cfg);
|
|
58
58
|
do {
|
|
59
59
|
const [res, err] = await this.wrapped.send(target, req, reqSchema, resSchema);
|
|
@@ -66,16 +66,13 @@ export const unaryWithBreaker = (
|
|
|
66
66
|
return new WithBreaker(base);
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
export const sendRequired = async <
|
|
70
|
-
RQ extends z.ZodTypeAny,
|
|
71
|
-
RS extends z.ZodTypeAny = RQ,
|
|
72
|
-
>(
|
|
69
|
+
export const sendRequired = async <RQ extends z.ZodType, RS extends z.ZodType = RQ>(
|
|
73
70
|
client: UnaryClient,
|
|
74
71
|
target: string,
|
|
75
|
-
req: z.input<RQ> | z.
|
|
72
|
+
req: z.input<RQ> | z.infer<RQ>,
|
|
76
73
|
reqSchema: RQ,
|
|
77
74
|
resSchema: RS,
|
|
78
|
-
): Promise<z.
|
|
75
|
+
): Promise<z.infer<RS>> => {
|
|
79
76
|
const [res, err] = await client.send(target, req, reqSchema, resSchema);
|
|
80
77
|
if (err != null) throw err;
|
|
81
78
|
return res;
|
package/src/websocket.ts
CHANGED
|
@@ -7,13 +7,7 @@
|
|
|
7
7
|
// License, use of this software will be governed by the Apache License, Version 2.0,
|
|
8
8
|
// included in the file licenses/APL.txt.
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
type binary,
|
|
12
|
-
buildQueryString,
|
|
13
|
-
errors,
|
|
14
|
-
runtime,
|
|
15
|
-
type URL,
|
|
16
|
-
} from "@synnaxlabs/x";
|
|
10
|
+
import { type binary, buildQueryString, errors, type URL } from "@synnaxlabs/x";
|
|
17
11
|
import { z } from "zod";
|
|
18
12
|
|
|
19
13
|
import { EOF, StreamClosed } from "@/errors";
|
|
@@ -21,12 +15,6 @@ import { CONTENT_TYPE_HEADER_KEY } from "@/http";
|
|
|
21
15
|
import { type Context, MiddlewareCollector } from "@/middleware";
|
|
22
16
|
import { type Stream, type StreamClient } from "@/stream";
|
|
23
17
|
|
|
24
|
-
const resolveWebSocketConstructor = (): ((target: string) => WebSocket) => {
|
|
25
|
-
if (runtime.RUNTIME !== "node") return (t) => new WebSocket(t);
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
27
|
-
return (t) => new (require("ws").WebSocket)(t, { rejectUnauthorized: false });
|
|
28
|
-
};
|
|
29
|
-
|
|
30
18
|
const wsMessageZ = z.object({
|
|
31
19
|
type: z.enum(["data", "close", "open"]),
|
|
32
20
|
payload: z.unknown(),
|
|
@@ -45,7 +33,7 @@ type ReceiveCallbacksQueue = Array<{
|
|
|
45
33
|
}>;
|
|
46
34
|
|
|
47
35
|
/** WebSocketStream is an implementation of Stream that is backed by a websocket. */
|
|
48
|
-
class WebSocketStream<RQ extends z.
|
|
36
|
+
class WebSocketStream<RQ extends z.ZodType, RS extends z.ZodType = RQ>
|
|
49
37
|
implements Stream<RQ, RS>
|
|
50
38
|
{
|
|
51
39
|
private readonly codec: binary.Codec;
|
|
@@ -77,7 +65,7 @@ class WebSocketStream<RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny = RQ>
|
|
|
77
65
|
}
|
|
78
66
|
|
|
79
67
|
/** Implements the Stream protocol */
|
|
80
|
-
send(req: z.input<RQ>): Error | null {
|
|
68
|
+
send(req: z.input<RQ> | z.infer<RQ>): Error | null {
|
|
81
69
|
if (this.serverClosed != null) return new EOF();
|
|
82
70
|
if (this.sendClosed) throw new StreamClosed();
|
|
83
71
|
this.ws.send(this.codec.encode({ type: "data", payload: req }));
|
|
@@ -85,12 +73,13 @@ class WebSocketStream<RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny = RQ>
|
|
|
85
73
|
}
|
|
86
74
|
|
|
87
75
|
/** Implements the Stream protocol */
|
|
88
|
-
async receive(): Promise<[z.
|
|
76
|
+
async receive(): Promise<[z.infer<RS>, null] | [null, Error]> {
|
|
89
77
|
if (this.serverClosed != null) return [null, this.serverClosed];
|
|
90
78
|
const msg = await this.receiveMsg();
|
|
91
79
|
if (msg.type === "close") {
|
|
92
80
|
if (msg.error == null) throw new Error("Message error must be defined");
|
|
93
81
|
this.serverClosed = errors.decode(msg.error);
|
|
82
|
+
if (this.serverClosed == null) throw new Error("Message error must be defined");
|
|
94
83
|
return [null, this.serverClosed];
|
|
95
84
|
}
|
|
96
85
|
return [this.resSchema.parse(msg.payload), null];
|
|
@@ -181,17 +170,16 @@ export class WebSocketClient extends MiddlewareCollector implements StreamClient
|
|
|
181
170
|
}
|
|
182
171
|
|
|
183
172
|
/** Implements the StreamClient interface. */
|
|
184
|
-
async stream<RQ extends z.
|
|
173
|
+
async stream<RQ extends z.ZodType, RS extends z.ZodType = RQ>(
|
|
185
174
|
target: string,
|
|
186
175
|
reqSchema: RQ,
|
|
187
176
|
resSchema: RS,
|
|
188
177
|
): Promise<Stream<RQ, RS>> {
|
|
189
|
-
const SocketConstructor = resolveWebSocketConstructor();
|
|
190
178
|
let stream: Stream<RQ, RS> | undefined;
|
|
191
179
|
const [, error] = await this.executeMiddleware(
|
|
192
180
|
{ target, protocol: "websocket", params: {}, role: "client" },
|
|
193
181
|
async (ctx: Context): Promise<[Context, Error | null]> => {
|
|
194
|
-
const ws =
|
|
182
|
+
const ws = new WebSocket(this.buildURL(target, ctx));
|
|
195
183
|
const outCtx: Context = { ...ctx, params: {} };
|
|
196
184
|
ws.binaryType = WebSocketClient.MESSAGE_TYPE;
|
|
197
185
|
const streamOrErr = await this.wrapSocket(ws, reqSchema, resSchema);
|
|
@@ -215,7 +203,7 @@ export class WebSocketClient extends MiddlewareCollector implements StreamClient
|
|
|
215
203
|
return this.baseUrl.child(target).toString() + qs;
|
|
216
204
|
}
|
|
217
205
|
|
|
218
|
-
private async wrapSocket<RQ extends z.
|
|
206
|
+
private async wrapSocket<RQ extends z.ZodType, RS extends z.ZodType = RQ>(
|
|
219
207
|
ws: WebSocket,
|
|
220
208
|
reqSchema: RQ,
|
|
221
209
|
resSchema: RS,
|
package/tsconfig.json
CHANGED
package/README.md
DELETED