@synnaxlabs/freighter 0.1.0 → 0.2.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/.editorconfig +15 -0
- package/.eslintrc.json +33 -0
- package/.gitignore +9 -0
- package/.nyc_output/3238f10e-9572-49ec-ab9d-28cbcaa6152a.json +1 -0
- package/.nyc_output/4e78a5c9-c0ca-4664-aa04-f478522608eb.json +1 -0
- package/.nyc_output/6a2244f2-5aea-45c7-8eeb-e14b454f0096.json +1 -0
- package/.nyc_output/dd1075a0-827b-4154-a75e-9bc90a4e16d0.json +1 -0
- package/.nyc_output/f829ad27-9bcd-4604-ae57-aae8c6f28d51.json +1 -0
- package/.nyc_output/fabc60f1-8fc5-4a1e-bea0-dc1fbcc31c9c.json +1 -0
- package/.nyc_output/processinfo/3238f10e-9572-49ec-ab9d-28cbcaa6152a.json +1 -0
- package/.nyc_output/processinfo/4e78a5c9-c0ca-4664-aa04-f478522608eb.json +1 -0
- package/.nyc_output/processinfo/6a2244f2-5aea-45c7-8eeb-e14b454f0096.json +1 -0
- package/.nyc_output/processinfo/dd1075a0-827b-4154-a75e-9bc90a4e16d0.json +1 -0
- package/.nyc_output/processinfo/f829ad27-9bcd-4604-ae57-aae8c6f28d51.json +1 -0
- package/.nyc_output/processinfo/fabc60f1-8fc5-4a1e-bea0-dc1fbcc31c9c.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.prettierignore +2 -0
- package/.prettierrc +3 -0
- package/.vscode/settings.json +4 -0
- package/build/main/index.d.ts +2 -0
- package/build/main/index.js +4 -2
- package/build/main/lib/caseconv.js +7 -7
- package/build/main/lib/encoder.d.ts +8 -9
- package/build/main/lib/encoder.js +3 -7
- package/build/main/lib/encoder.spec.js +2 -2
- package/build/main/lib/errors.d.ts +34 -24
- package/build/main/lib/errors.js +117 -99
- package/build/main/lib/errors.spec.js +63 -51
- package/build/main/lib/http.d.ts +11 -7
- package/build/main/lib/http.js +44 -29
- package/build/main/lib/http.spec.js +14 -4
- package/build/main/lib/middleware.d.ts +45 -0
- package/build/main/lib/middleware.js +38 -0
- package/build/main/lib/runtime.d.ts +2 -2
- package/build/main/lib/runtime.js +17 -15
- package/build/main/lib/stream.d.ts +2 -1
- package/build/main/lib/transport.d.ts +13 -0
- package/build/main/lib/transport.js +3 -0
- package/build/main/lib/unary.d.ts +2 -1
- package/build/main/lib/url.d.ts +30 -29
- package/build/main/lib/url.js +54 -46
- package/build/main/lib/url.spec.js +35 -30
- package/build/main/lib/util/log.d.ts +2 -0
- package/build/main/lib/util/log.js +15 -0
- package/build/main/lib/websocket.d.ts +7 -3
- package/build/main/lib/websocket.js +37 -19
- package/build/main/lib/websocket.spec.d.ts +1 -0
- package/build/main/lib/websocket.spec.js +86 -0
- package/build/main/lib/ws.spec.js +96 -54
- package/build/module/index.d.ts +2 -0
- package/build/module/index.js +2 -1
- package/build/module/lib/caseconv.js +8 -5
- package/build/module/lib/encoder.d.ts +8 -9
- package/build/module/lib/encoder.js +3 -7
- package/build/module/lib/encoder.spec.js +2 -2
- package/build/module/lib/errors.d.ts +34 -24
- package/build/module/lib/errors.js +101 -97
- package/build/module/lib/errors.spec.js +64 -50
- package/build/module/lib/http.d.ts +11 -7
- package/build/module/lib/http.js +42 -28
- package/build/module/lib/http.spec.js +14 -4
- package/build/module/lib/middleware.d.ts +45 -0
- package/build/module/lib/middleware.js +32 -0
- package/build/module/lib/runtime.d.ts +2 -2
- package/build/module/lib/runtime.js +14 -12
- package/build/module/lib/stream.d.ts +2 -1
- package/build/module/lib/transport.d.ts +13 -0
- package/build/module/lib/transport.js +2 -0
- package/build/module/lib/unary.d.ts +2 -1
- package/build/module/lib/url.d.ts +30 -29
- package/build/module/lib/url.js +55 -51
- package/build/module/lib/url.spec.js +26 -23
- package/build/module/lib/util/log.d.ts +2 -0
- package/build/module/lib/util/log.js +11 -0
- package/build/module/lib/websocket.d.ts +7 -3
- package/build/module/lib/websocket.js +40 -19
- package/build/module/lib/websocket.spec.d.ts +1 -0
- package/build/module/lib/websocket.spec.js +82 -0
- package/build/module/lib/ws.spec.js +68 -43
- package/build/tsconfig.module.tsbuildinfo +1 -0
- package/build/tsconfig.tsbuildinfo +1 -0
- package/coverage/base.css +224 -0
- package/coverage/block-navigation.js +87 -0
- package/coverage/caseconv.ts.html +124 -0
- package/coverage/encoder.ts.html +403 -0
- package/coverage/errors.ts.html +727 -0
- package/coverage/favicon.png +0 -0
- package/coverage/http.ts.html +532 -0
- package/coverage/index.html +221 -0
- package/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/caseconv.ts.html +124 -0
- package/coverage/lcov-report/encoder.ts.html +403 -0
- package/coverage/lcov-report/errors.ts.html +727 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/http.ts.html +532 -0
- package/coverage/lcov-report/index.html +221 -0
- package/coverage/lcov-report/middleware.ts.html +286 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/runtime.ts.html +154 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov-report/url.ts.html +322 -0
- package/coverage/lcov-report/websocket.ts.html +760 -0
- package/coverage/lcov.info +552 -0
- package/coverage/middleware.ts.html +286 -0
- package/coverage/prettify.css +1 -0
- package/coverage/prettify.js +2 -0
- package/coverage/runtime.ts.html +154 -0
- package/coverage/sort-arrow-sprite.png +0 -0
- package/coverage/sorter.js +196 -0
- package/coverage/url.ts.html +322 -0
- package/coverage/websocket.ts.html +760 -0
- package/package.json +3 -2
- package/src/index.ts +25 -0
- package/src/lib/caseconv.ts +13 -0
- package/src/lib/encoder.spec.ts +23 -0
- package/src/lib/encoder.ts +105 -0
- package/src/lib/errors.spec.ts +98 -0
- package/src/lib/errors.ts +214 -0
- package/src/lib/http.spec.ts +85 -0
- package/src/lib/http.ts +149 -0
- package/src/lib/middleware.ts +67 -0
- package/src/lib/runtime.ts +23 -0
- package/src/lib/stream.ts +88 -0
- package/src/lib/transport.ts +14 -0
- package/src/lib/unary.ts +21 -0
- package/src/lib/url.spec.ts +37 -0
- package/src/lib/url.ts +79 -0
- package/src/lib/util/log.ts +12 -0
- package/src/lib/websocket.spec.ts +106 -0
- package/src/lib/websocket.ts +225 -0
- package/src/types/example.d.ts +24 -0
- package/tsconfig.json +47 -0
- package/tsconfig.module.json +9 -0
- package/yarn.lock +5878 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { z, ZodSchema } from 'zod';
|
|
2
|
+
|
|
3
|
+
import { EncoderDecoder } from './encoder';
|
|
4
|
+
import { decodeError, EOF, ErrorPayloadSchema, StreamClosed } from './errors';
|
|
5
|
+
import { buildQueryString } from './http';
|
|
6
|
+
import { CONTENT_TYPE_HEADER_KEY } from './http';
|
|
7
|
+
import { MetaData, MiddlewareCollector } from './middleware';
|
|
8
|
+
import { Runtime, RUNTIME } from './runtime';
|
|
9
|
+
import { Stream, StreamClient } from './stream';
|
|
10
|
+
import URL from './url';
|
|
11
|
+
|
|
12
|
+
const resolveWebSocketConstructor = (): typeof WebSocket => {
|
|
13
|
+
if (RUNTIME == Runtime.Node) return require('ws');
|
|
14
|
+
return WebSocket;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
enum MessageType {
|
|
18
|
+
Data = 'data',
|
|
19
|
+
Close = 'close',
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const MessageSchema = z.object({
|
|
23
|
+
type: z.nativeEnum(MessageType),
|
|
24
|
+
payload: z.unknown().optional(),
|
|
25
|
+
error: z.optional(ErrorPayloadSchema),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
type Message = z.infer<typeof MessageSchema>;
|
|
29
|
+
|
|
30
|
+
enum CloseCode {
|
|
31
|
+
Normal = 1000,
|
|
32
|
+
GoingAway = 1001,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** WebSocketStream is an implementation of Stream that is backed by a websocket. */
|
|
36
|
+
class WebSocketStream<RQ, RS> implements Stream<RQ, RS> {
|
|
37
|
+
private encoder: EncoderDecoder;
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
private reqSchema: z.ZodSchema<RQ>;
|
|
41
|
+
private resSchema: z.ZodSchema<RS>;
|
|
42
|
+
|
|
43
|
+
private ws: WebSocket;
|
|
44
|
+
private server_closed?: Error;
|
|
45
|
+
private send_closed: boolean;
|
|
46
|
+
private receiveDataQueue: Message[] = [];
|
|
47
|
+
private receiveCallbacksQueue: {
|
|
48
|
+
resolve: (msg: Message) => void;
|
|
49
|
+
reject: (reason: unknown) => void;
|
|
50
|
+
}[] = [];
|
|
51
|
+
|
|
52
|
+
constructor(
|
|
53
|
+
ws: WebSocket,
|
|
54
|
+
encoder: EncoderDecoder,
|
|
55
|
+
reqSchema: z.ZodSchema<RQ>,
|
|
56
|
+
resSchema: z.ZodSchema<RS>
|
|
57
|
+
) {
|
|
58
|
+
this.encoder = encoder;
|
|
59
|
+
this.reqSchema = reqSchema;
|
|
60
|
+
this.resSchema = resSchema;
|
|
61
|
+
this.ws = ws;
|
|
62
|
+
this.send_closed = false;
|
|
63
|
+
this.listenForMessages();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Implements the Stream protocol */
|
|
67
|
+
send(req: RQ): Error | undefined {
|
|
68
|
+
if (this.server_closed) return new EOF();
|
|
69
|
+
if (this.send_closed) throw new StreamClosed();
|
|
70
|
+
|
|
71
|
+
this.ws.send(
|
|
72
|
+
this.encoder.encode({
|
|
73
|
+
type: MessageType.Data,
|
|
74
|
+
payload: req,
|
|
75
|
+
error: undefined,
|
|
76
|
+
})
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** Implements the Stream protocol */
|
|
83
|
+
async receive(): Promise<[RS | undefined, Error | undefined]> {
|
|
84
|
+
if (this.server_closed) return [undefined, this.server_closed];
|
|
85
|
+
|
|
86
|
+
const msg = await this.receiveMsg();
|
|
87
|
+
|
|
88
|
+
if (msg.type == MessageType.Close) {
|
|
89
|
+
if (!msg.error) throw new Error('Message error must be defined');
|
|
90
|
+
this.server_closed = decodeError(msg.error);
|
|
91
|
+
return [undefined, this.server_closed];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return [this.resSchema.parse(msg.payload), undefined];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** Implements the Stream protocol */
|
|
98
|
+
received(): boolean {
|
|
99
|
+
return this.receiveDataQueue.length !== 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Implements the Stream protocol */
|
|
103
|
+
closeSend(): void {
|
|
104
|
+
if (this.send_closed || this.server_closed) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
const msg: Message = { type: MessageType.Close };
|
|
108
|
+
try {
|
|
109
|
+
this.ws.send(this.encoder.encode(msg));
|
|
110
|
+
} finally {
|
|
111
|
+
this.send_closed = true;
|
|
112
|
+
}
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async receiveMsg(): Promise<Message> {
|
|
117
|
+
if (this.receiveDataQueue.length !== 0) {
|
|
118
|
+
const msg = this.receiveDataQueue.shift();
|
|
119
|
+
if (msg) return msg;
|
|
120
|
+
throw new Error('unexpected undefined message');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return new Promise((resolve, reject) => {
|
|
124
|
+
this.receiveCallbacksQueue.push({ resolve, reject });
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
private listenForMessages(): void {
|
|
129
|
+
this.ws.onmessage = (ev: MessageEvent) => {
|
|
130
|
+
const msg = this.encoder.decode(ev.data, MessageSchema);
|
|
131
|
+
|
|
132
|
+
if (this.receiveCallbacksQueue.length > 0) {
|
|
133
|
+
const callback = this.receiveCallbacksQueue.shift();
|
|
134
|
+
if (callback) callback.resolve(msg);
|
|
135
|
+
else throw new Error('Unexpected empty callback queue');
|
|
136
|
+
} else {
|
|
137
|
+
this.receiveDataQueue.push(msg);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
this.ws.onclose = (ev: CloseEvent) => {
|
|
142
|
+
if ([CloseCode.Normal, CloseCode.GoingAway].includes(ev.code)) {
|
|
143
|
+
this.server_closed = new EOF();
|
|
144
|
+
} else {
|
|
145
|
+
this.server_closed = new StreamClosed();
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export const FREIGHTER_METADATA_PREFIX = 'freightermd';
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* WebSocketClient is an implementation of StreamClient that is backed by
|
|
155
|
+
* websockets.
|
|
156
|
+
*/
|
|
157
|
+
export class WebSocketClient
|
|
158
|
+
extends MiddlewareCollector
|
|
159
|
+
implements StreamClient
|
|
160
|
+
{
|
|
161
|
+
baseUrl: URL;
|
|
162
|
+
encoder: EncoderDecoder;
|
|
163
|
+
|
|
164
|
+
static readonly MESSAGE_TYPE = 'arraybuffer';
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param encoder - The encoder to use for encoding messages and decoding
|
|
168
|
+
* responses.
|
|
169
|
+
* @param baseURL - A base url to use as a prefix for all requests.
|
|
170
|
+
*/
|
|
171
|
+
constructor(encoder: EncoderDecoder, baseURL: URL) {
|
|
172
|
+
super();
|
|
173
|
+
this.baseUrl = baseURL.replace({ protocol: 'ws' });
|
|
174
|
+
this.encoder = encoder;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/** Implements the StreamClient interface. */
|
|
178
|
+
async stream<RQ, RS>(
|
|
179
|
+
target: string,
|
|
180
|
+
reqSchema: ZodSchema<RQ>,
|
|
181
|
+
resSchema: ZodSchema<RS>
|
|
182
|
+
): Promise<Stream<RQ, RS>> {
|
|
183
|
+
const socketConstructor = resolveWebSocketConstructor();
|
|
184
|
+
let stream: Stream<RQ, RS> | undefined;
|
|
185
|
+
const error = await this.executeMiddleware(
|
|
186
|
+
{ target, protocol: 'websocket', params: {} },
|
|
187
|
+
async (md: MetaData): Promise<Error | undefined> => {
|
|
188
|
+
const ws = new socketConstructor(this.buildURL(target, md));
|
|
189
|
+
ws.binaryType = WebSocketClient.MESSAGE_TYPE;
|
|
190
|
+
const streamOrErr = await this.wrapSocket(ws, reqSchema, resSchema);
|
|
191
|
+
if (streamOrErr instanceof Error) return streamOrErr;
|
|
192
|
+
stream = streamOrErr;
|
|
193
|
+
return undefined;
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
if (error) throw error;
|
|
197
|
+
return stream as Stream<RQ, RS>;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
private buildURL(target: string, md: MetaData): string {
|
|
201
|
+
const qs = buildQueryString({
|
|
202
|
+
request: {
|
|
203
|
+
[CONTENT_TYPE_HEADER_KEY]: this.encoder.contentType,
|
|
204
|
+
...md.params,
|
|
205
|
+
},
|
|
206
|
+
prefix: FREIGHTER_METADATA_PREFIX,
|
|
207
|
+
});
|
|
208
|
+
return this.baseUrl.child(target).stringify() + qs;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
private async wrapSocket<RQ, RS>(
|
|
212
|
+
ws: WebSocket,
|
|
213
|
+
reqSchema: ZodSchema<RQ>,
|
|
214
|
+
resSchema: ZodSchema<RS>
|
|
215
|
+
): Promise<WebSocketStream<RQ, RS> | Error> {
|
|
216
|
+
return await new Promise((resolve, reject) => {
|
|
217
|
+
ws.onopen = () => {
|
|
218
|
+
resolve(
|
|
219
|
+
new WebSocketStream<RQ, RS>(ws, this.encoder, reqSchema, resSchema)
|
|
220
|
+
);
|
|
221
|
+
};
|
|
222
|
+
ws.onerror = (ev: Event) => reject(new Error(ev.toString()));
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* If you import a dependency which does not include its own type definitions,
|
|
3
|
+
* TypeScript will try to find a definition for it by following the `typeRoots`
|
|
4
|
+
* compiler option in tsconfig.json. For this project, we've configured it to
|
|
5
|
+
* fall back to this folder if nothing is found in node_modules/@types.
|
|
6
|
+
*
|
|
7
|
+
* Often, you can install the DefinitelyTyped
|
|
8
|
+
* (https://github.com/DefinitelyTyped/DefinitelyTyped) type definition for the
|
|
9
|
+
* dependency in question. However, if no one has yet contributed definitions
|
|
10
|
+
* for the package, you may want to declare your own. (If you're using the
|
|
11
|
+
* `noImplicitAny` compiler options, you'll be required to declare it.)
|
|
12
|
+
*
|
|
13
|
+
* This is an example type definition which allows import from `module-name`,
|
|
14
|
+
* e.g.:
|
|
15
|
+
* ```ts
|
|
16
|
+
* import something from 'module-name';
|
|
17
|
+
* something();
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
declare module 'module-name' {
|
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
22
|
+
const whatever: any;
|
|
23
|
+
export = whatever;
|
|
24
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"incremental": true,
|
|
4
|
+
"target": "es2017",
|
|
5
|
+
"outDir": "build/main",
|
|
6
|
+
"rootDir": "src",
|
|
7
|
+
"moduleResolution": "node",
|
|
8
|
+
"module": "commonjs",
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"inlineSourceMap": true,
|
|
11
|
+
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
|
|
12
|
+
"resolveJsonModule": true /* Include modules imported with .json extension. */,
|
|
13
|
+
|
|
14
|
+
"strict": true /* Enable all strict type-checking options. */,
|
|
15
|
+
|
|
16
|
+
/* Strict Type-Checking Options */
|
|
17
|
+
// "noImplicitAny": true /* Raise error on expressions and declarations with an implied 'any' type. */,
|
|
18
|
+
// "strictNullChecks": true /* Enable strict null checks. */,
|
|
19
|
+
// "strictFunctionTypes": true /* Enable strict checking of function types. */,
|
|
20
|
+
// "strictPropertyInitialization": true /* Enable strict checking of property initialization in classes. */,
|
|
21
|
+
// "noImplicitThis": true /* Raise error on 'this' expressions with an implied 'any' type. */,
|
|
22
|
+
// "alwaysStrict": true /* Parse in strict mode and emit "use strict" for each source file. */,
|
|
23
|
+
|
|
24
|
+
/* Additional Checks */
|
|
25
|
+
"noUnusedLocals": true /* Report errors on unused locals. */,
|
|
26
|
+
"noUnusedParameters": true /* Report errors on unused parameters. */,
|
|
27
|
+
"noImplicitReturns": true /* Report error when not all code paths in function return a value. */,
|
|
28
|
+
"noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */,
|
|
29
|
+
|
|
30
|
+
/* Debugging Options */
|
|
31
|
+
"traceResolution": false /* Report module resolution log messages. */,
|
|
32
|
+
"listEmittedFiles": false /* Print names of generated files part of the compilation. */,
|
|
33
|
+
"listFiles": false /* Print names of files part of the compilation. */,
|
|
34
|
+
"pretty": true /* Stylize errors and messages using color and context. */,
|
|
35
|
+
|
|
36
|
+
/* Experimental Options */
|
|
37
|
+
// "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
|
|
38
|
+
// "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
|
|
39
|
+
|
|
40
|
+
"lib": ["es2017", "dom"],
|
|
41
|
+
"types": ["node"],
|
|
42
|
+
"typeRoots": ["node_modules/@types", "src/types"]
|
|
43
|
+
},
|
|
44
|
+
"include": ["src/**/*.ts"],
|
|
45
|
+
"exclude": ["node_modules/**"],
|
|
46
|
+
"compileOnSave": false
|
|
47
|
+
}
|