@synnaxlabs/freighter 0.35.0 → 0.36.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/dist/freighter.cjs +8 -8
- package/dist/freighter.js +510 -364
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/unary.d.ts +2 -0
- package/dist/unary.d.ts.map +1 -1
- package/dist/websocket.d.ts.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +1 -1
- package/src/unary.ts +35 -0
- package/src/websocket.spec.ts +20 -11
- package/src/websocket.ts +18 -2
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,6 @@ export { HTTPClient } from './http';
|
|
|
4
4
|
export type { Context, Middleware, Next } from './middleware';
|
|
5
5
|
export type { Stream, StreamClient } from './stream';
|
|
6
6
|
export type { UnaryClient } from './unary';
|
|
7
|
-
export { sendRequired } from './unary';
|
|
7
|
+
export { sendRequired, unaryWithBreaker } from './unary';
|
|
8
8
|
export { WebSocketClient } from './websocket';
|
|
9
9
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EACL,cAAc,EACd,WAAW,EACX,WAAW,EACX,GAAG,EACH,YAAY,EACZ,MAAM,EACN,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AASA,YAAY,EAAE,YAAY,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EACL,cAAc,EACd,WAAW,EACX,WAAW,EACX,GAAG,EACH,YAAY,EACZ,MAAM,EACN,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AACpC,YAAY,EAAE,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC9D,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACrD,YAAY,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/unary.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { Transport } from './transport';
|
|
3
|
+
import { breaker } from '@synnaxlabs/x';
|
|
3
4
|
/**
|
|
4
5
|
* An interface for an entity that implements a simple request-response
|
|
5
6
|
* transport between two entities.
|
|
@@ -13,5 +14,6 @@ export interface UnaryClient extends Transport {
|
|
|
13
14
|
*/
|
|
14
15
|
send: <RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny = RQ>(target: string, req: z.input<RQ> | z.output<RQ>, reqSchema: RQ, resSchema: RS) => Promise<[z.output<RS>, null] | [null, Error]>;
|
|
15
16
|
}
|
|
17
|
+
export declare const unaryWithBreaker: (base: UnaryClient, cfg: breaker.Config) => UnaryClient;
|
|
16
18
|
export declare const sendRequired: <RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny = RQ>(client: UnaryClient, target: string, req: z.input<RQ> | z.output<RQ>, reqSchema: RQ, resSchema: RS) => Promise<z.output<RS>>;
|
|
17
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,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"unary.d.ts","sourceRoot":"","sources":["../src/unary.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAIxC;;;GAGG;AACH,MAAM,WAAW,WAAY,SAAQ,SAAS;IAC5C;;;;;OAKG;IACH,IAAI,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU,GAAG,EAAE,EAC1D,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAC/B,SAAS,EAAE,EAAE,EACb,SAAS,EAAE,EAAE,KACV,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;CACpD;AAED,eAAO,MAAM,gBAAgB,SACrB,WAAW,OACZ,OAAO,CAAC,MAAM,KAClB,WA2BF,CAAC;AAEF,eAAO,MAAM,YAAY,GACvB,EAAE,SAAS,CAAC,CAAC,UAAU,EACvB,EAAE,SAAS,CAAC,CAAC,UAAU,eAEf,WAAW,UACX,MAAM,OACT,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,aACpB,EAAE,aACF,EAAE,KACZ,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,EAAE,CAAC,CAItB,CAAC"}
|
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,EAAE,KAAK,MAAM,EAA6B,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AACjF,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;
|
|
1
|
+
{"version":3,"file":"websocket.d.ts","sourceRoot":"","sources":["../src/websocket.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,KAAK,MAAM,EAA6B,KAAK,GAAG,EAAE,MAAM,eAAe,CAAC;AACjF,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;AAoH1D,eAAO,MAAM,yBAAyB,iBAAiB,CAAC;AAQxD;;;GAGG;AACH,qBAAa,eAAgB,SAAQ,mBAAoB,YAAW,YAAY;IAC9E,OAAO,EAAE,GAAG,CAAC;IACb,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;IAEtB,MAAM,CAAC,QAAQ,CAAC,YAAY,iBAAiB;IAE7C;;;;OAIG;gBACS,YAAY,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,MAAM,UAAQ;IAMpE,6CAA6C;IACvC,MAAM,CAAC,EAAE,SAAS,CAAC,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,CAAC,UAAU,GAAG,EAAE,EAChE,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,EAAE,EACb,SAAS,EAAE,EAAE,GACZ,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;IAmB1B,OAAO,CAAC,QAAQ;YAWF,UAAU;CAsBzB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@synnaxlabs/freighter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.36.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "a modular transport abstraction",
|
|
6
6
|
"repository": "https://github.com/synnaxlabs/synnax/tree/main/freighter/ts",
|
|
@@ -16,19 +16,19 @@
|
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"node-fetch": "^2.7.0",
|
|
18
18
|
"ws": "^8.18.0",
|
|
19
|
-
"zod": "^3.
|
|
20
|
-
"@synnaxlabs/
|
|
21
|
-
"@synnaxlabs/
|
|
19
|
+
"zod": "^3.24.1",
|
|
20
|
+
"@synnaxlabs/alamos": "0.36.0",
|
|
21
|
+
"@synnaxlabs/x": "0.36.0"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^22.7.5",
|
|
25
|
-
"@vitest/coverage-v8": "^2.1.
|
|
26
|
-
"eslint": "^9.
|
|
25
|
+
"@vitest/coverage-v8": "^2.1.8",
|
|
26
|
+
"eslint": "^9.16.0",
|
|
27
27
|
"typescript": "^5.6.3",
|
|
28
|
-
"vite": "^
|
|
29
|
-
"vitest": "^2.1.
|
|
30
|
-
"@synnaxlabs/vite-plugin": "0.0.1",
|
|
28
|
+
"vite": "^6.0.3",
|
|
29
|
+
"vitest": "^2.1.8",
|
|
31
30
|
"@synnaxlabs/tsconfig": "0.0.2",
|
|
31
|
+
"@synnaxlabs/vite-plugin": "0.0.1",
|
|
32
32
|
"eslint-config-synnaxlabs": "0.0.1"
|
|
33
33
|
},
|
|
34
34
|
"main": "dist/freighter.cjs",
|
package/src/index.ts
CHANGED
|
@@ -23,5 +23,5 @@ export { HTTPClient } from "@/http";
|
|
|
23
23
|
export type { Context, Middleware, Next } from "@/middleware";
|
|
24
24
|
export type { Stream, StreamClient } from "@/stream";
|
|
25
25
|
export type { UnaryClient } from "@/unary";
|
|
26
|
-
export { sendRequired } from "@/unary";
|
|
26
|
+
export { sendRequired, unaryWithBreaker } from "@/unary";
|
|
27
27
|
export { WebSocketClient } from "@/websocket";
|
package/src/unary.ts
CHANGED
|
@@ -10,6 +10,9 @@
|
|
|
10
10
|
import { type z } from "zod";
|
|
11
11
|
|
|
12
12
|
import { type Transport } from "@/transport";
|
|
13
|
+
import { breaker } from "@synnaxlabs/x";
|
|
14
|
+
import { Middleware } from "@/middleware";
|
|
15
|
+
import { Unreachable } from "@/errors";
|
|
13
16
|
|
|
14
17
|
/**
|
|
15
18
|
* An interface for an entity that implements a simple request-response
|
|
@@ -30,6 +33,38 @@ export interface UnaryClient extends Transport {
|
|
|
30
33
|
) => Promise<[z.output<RS>, null] | [null, Error]>;
|
|
31
34
|
}
|
|
32
35
|
|
|
36
|
+
export const unaryWithBreaker = (
|
|
37
|
+
base: UnaryClient,
|
|
38
|
+
cfg: breaker.Config,
|
|
39
|
+
): UnaryClient => {
|
|
40
|
+
class WithBreaker implements UnaryClient {
|
|
41
|
+
readonly wrapped: UnaryClient;
|
|
42
|
+
|
|
43
|
+
constructor(wrapper: UnaryClient) {
|
|
44
|
+
this.wrapped = wrapper;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
use(...mw: Middleware[]) {
|
|
48
|
+
this.wrapped.use(...mw);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async send<RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny = RQ>(
|
|
52
|
+
target: string,
|
|
53
|
+
req: z.input<RQ> | z.output<RQ>,
|
|
54
|
+
reqSchema: RQ,
|
|
55
|
+
resSchema: RS,
|
|
56
|
+
): Promise<[z.output<RS>, null] | [null, Error]> {
|
|
57
|
+
const brk = breaker.create(cfg);
|
|
58
|
+
do {
|
|
59
|
+
const [res, err] = await this.wrapped.send(target, req, reqSchema, resSchema);
|
|
60
|
+
if (err == null || !Unreachable.matches(err)) return [res, err];
|
|
61
|
+
if (!(await brk())) return [res, err];
|
|
62
|
+
} while (true);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return new WithBreaker(base);
|
|
66
|
+
};
|
|
67
|
+
|
|
33
68
|
export const sendRequired = async <
|
|
34
69
|
RQ extends z.ZodTypeAny,
|
|
35
70
|
RS extends z.ZodTypeAny = RQ,
|
package/src/websocket.spec.ts
CHANGED
|
@@ -103,17 +103,26 @@ describe("websocket", () => {
|
|
|
103
103
|
expect(response).toBeNull();
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
106
|
+
describe("middleware", () => {
|
|
107
|
+
test("receive middleware", async () => {
|
|
108
|
+
const myClient = new WebSocketClient(url, new binary.JSONCodec());
|
|
109
|
+
let c = 0;
|
|
110
|
+
myClient.use(async (md, next): Promise<[Context, Error | null]> => {
|
|
111
|
+
if (md.params !== undefined) {
|
|
112
|
+
c++;
|
|
113
|
+
md.params.Test = "test";
|
|
114
|
+
}
|
|
115
|
+
return await next(md);
|
|
116
|
+
});
|
|
117
|
+
await myClient.stream("stream/middlewareCheck", MessageSchema, MessageSchema);
|
|
118
|
+
expect(c).toEqual(1);
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("middleware error on server", async () => {
|
|
122
|
+
const myClient = new WebSocketClient(url, new binary.JSONCodec());
|
|
123
|
+
await expect(
|
|
124
|
+
myClient.stream("stream/middlewareCheck", MessageSchema, MessageSchema),
|
|
125
|
+
).rejects.toThrow("test param not found");
|
|
115
126
|
});
|
|
116
|
-
await myClient.stream("stream/middlewareCheck", MessageSchema, MessageSchema);
|
|
117
|
-
expect(c).toEqual(1);
|
|
118
127
|
});
|
|
119
128
|
});
|
package/src/websocket.ts
CHANGED
|
@@ -22,7 +22,7 @@ const resolveWebSocketConstructor = (): ((target: string) => WebSocket) => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const MessageSchema = z.object({
|
|
25
|
-
type: z.union([z.literal("data"), z.literal("close")]),
|
|
25
|
+
type: z.union([z.literal("data"), z.literal("close"), z.literal("open")]),
|
|
26
26
|
payload: z.unknown().optional(),
|
|
27
27
|
error: z.optional(errorZ),
|
|
28
28
|
});
|
|
@@ -57,6 +57,15 @@ class WebSocketStream<RQ extends z.ZodTypeAny, RS extends z.ZodTypeAny = RQ>
|
|
|
57
57
|
this.listenForMessages();
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async receiveOpenAck(): Promise<Error | null> {
|
|
61
|
+
const msg = await this.receiveMsg();
|
|
62
|
+
if (msg.type !== "open") {
|
|
63
|
+
if (msg.error == null) throw new Error("Message error must be defined");
|
|
64
|
+
return decodeError(msg.error);
|
|
65
|
+
}
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
/** Implements the Stream protocol */
|
|
61
70
|
send(req: z.input<RQ>): Error | null {
|
|
62
71
|
if (this.serverClosed != null) return new EOF();
|
|
@@ -191,7 +200,14 @@ export class WebSocketClient extends MiddlewareCollector implements StreamClient
|
|
|
191
200
|
): Promise<WebSocketStream<RQ, RS> | Error> {
|
|
192
201
|
return await new Promise((resolve) => {
|
|
193
202
|
ws.onopen = () => {
|
|
194
|
-
|
|
203
|
+
const oWs = new WebSocketStream<RQ, RS>(ws, this.encoder, reqSchema, resSchema);
|
|
204
|
+
oWs
|
|
205
|
+
.receiveOpenAck()
|
|
206
|
+
.then((err) => {
|
|
207
|
+
if (err != null) resolve(err);
|
|
208
|
+
else resolve(oWs);
|
|
209
|
+
})
|
|
210
|
+
.catch((err) => resolve(err));
|
|
195
211
|
};
|
|
196
212
|
ws.onerror = (ev: Event) => {
|
|
197
213
|
const ev_ = ev as ErrorEvent;
|