@nmtjs/protocol 0.12.6 → 0.12.7

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.
@@ -1,634 +0,0 @@
1
- import {
2
- createPromise,
3
- type InteractivePromise,
4
- type OneOf,
5
- } from '@nmtjs/common'
6
- import { concat, decodeNumber, encodeNumber } from '../common/binary.ts'
7
- import type { ProtocolBlobMetadata } from '../common/blob.ts'
8
- import {
9
- ClientMessageType,
10
- ErrorCode,
11
- ServerMessageType,
12
- } from '../common/enums.ts'
13
- import type {
14
- BaseProtocolError,
15
- ProtocolRPC,
16
- ProtocolRPCResponse,
17
- } from '../common/types.ts'
18
- import { EventEmitter } from './events.ts'
19
- import type { BaseClientFormat } from './format.ts'
20
- import {
21
- ProtocolClientBlobStream,
22
- ProtocolServerBlobStream,
23
- ProtocolServerStream,
24
- } from './stream.ts'
25
-
26
- export class ProtocolError extends Error implements BaseProtocolError {
27
- code: string
28
- data?: any
29
-
30
- constructor(code: string, message?: string, data?: any) {
31
- super(message)
32
- this.code = code
33
- this.data = data
34
- }
35
-
36
- get message() {
37
- return `${this.code} ${super.message}`
38
- }
39
-
40
- toString() {
41
- return `${this.code} ${this.message}`
42
- }
43
-
44
- toJSON() {
45
- return {
46
- code: this.code,
47
- message: this.message,
48
- data: this.data,
49
- }
50
- }
51
- }
52
-
53
- export class ProtocolClientStreams {
54
- readonly #collection = new Map<number, ProtocolClientBlobStream>()
55
-
56
- get(streamId: number) {
57
- const stream = this.#collection.get(streamId)
58
- if (!stream) throw new Error('Stream not found')
59
- return stream
60
- }
61
-
62
- add(
63
- source: ReadableStream,
64
- streamId: number,
65
- metadata: ProtocolBlobMetadata,
66
- ) {
67
- const stream = new ProtocolClientBlobStream(source, streamId, metadata)
68
- this.#collection.set(streamId, stream)
69
- return stream
70
- }
71
-
72
- remove(streamId: number) {
73
- this.#collection.delete(streamId)
74
- }
75
-
76
- abort(streamId: number, error?: Error) {
77
- const stream = this.get(streamId)
78
- stream.abort(error)
79
- this.remove(streamId)
80
- }
81
-
82
- pull(streamId: number, size: number) {
83
- const stream = this.get(streamId)
84
- return stream.read(size)
85
- }
86
-
87
- end(streamId: number) {
88
- this.get(streamId).end()
89
- this.remove(streamId)
90
- }
91
-
92
- clear(error?: Error) {
93
- if (error) {
94
- for (const stream of this.#collection.values()) {
95
- stream.abort(error)
96
- }
97
- }
98
- this.#collection.clear()
99
- }
100
- }
101
-
102
- export class ProtocolServerStreams<
103
- T extends ProtocolServerStream = ProtocolServerStream,
104
- > {
105
- readonly #collection = new Map<number, T>()
106
-
107
- has(streamId: number) {
108
- return this.#collection.has(streamId)
109
- }
110
-
111
- get(streamId: number) {
112
- const stream = this.#collection.get(streamId)
113
- if (!stream) throw new Error('Stream not found')
114
- return stream
115
- }
116
-
117
- add(streamId: number, stream: T) {
118
- this.#collection.set(streamId, stream)
119
- return stream
120
- }
121
-
122
- remove(streamId: number) {
123
- this.#collection.delete(streamId)
124
- }
125
-
126
- abort(streamId: number) {
127
- if (this.has(streamId)) {
128
- const stream = this.get(streamId)
129
- stream.abort()
130
- this.remove(streamId)
131
- }
132
- }
133
-
134
- async push(streamId: number, chunk: ArrayBuffer) {
135
- const stream = this.get(streamId)
136
- return await stream.push(chunk)
137
- }
138
-
139
- end(streamId: number) {
140
- const stream = this.get(streamId)
141
- stream.end()
142
- this.remove(streamId)
143
- }
144
-
145
- clear(error?: Error) {
146
- if (error) {
147
- for (const stream of this.#collection.values()) {
148
- stream.abort(error)
149
- }
150
- }
151
- this.#collection.clear()
152
- }
153
- }
154
-
155
- export type ProtocolTransportEventMap = {
156
- connected: []
157
- disconnected: []
158
- }
159
-
160
- export interface ProtocolSendMetadata {
161
- callId?: number
162
- streamId?: number
163
- }
164
-
165
- export enum ProtocolTransportStatus {
166
- CONNECTED = 'CONNECTED',
167
- DISCONNECTED = 'DISCONNECTED',
168
- CONNECTING = 'CONNECTING',
169
- }
170
-
171
- export abstract class ProtocolTransport extends EventEmitter<ProtocolTransportEventMap> {
172
- status: ProtocolTransportStatus = ProtocolTransportStatus.DISCONNECTED
173
-
174
- abstract connect(
175
- auth: any,
176
- transformer: ProtocolBaseTransformer,
177
- ): Promise<void>
178
- abstract disconnect(): Promise<void>
179
- abstract call(
180
- namespace: string,
181
- procedure: string,
182
- payload: any,
183
- options: ProtocolBaseClientCallOptions,
184
- transformer: ProtocolBaseTransformer,
185
- ): Promise<ProtocolClientCall>
186
- abstract send(
187
- messageType: ClientMessageType,
188
- buffer: ArrayBuffer,
189
- metadata: ProtocolSendMetadata,
190
- ): Promise<void>
191
- }
192
-
193
- export class ProtocolBaseTransformer {
194
- encodeRPC(namespace: string, procedure: string, payload: any) {
195
- return payload
196
- }
197
- decodeRPC(namespace: string, procedure: string, payload: any) {
198
- return payload
199
- }
200
- decodeRPCChunk(namespace: string, procedure: string, payload: any) {
201
- return payload
202
- }
203
- decodeEvent(namespace: string, event: string, payload: any) {
204
- return payload
205
- }
206
- }
207
-
208
- export type ProtocolClientCall = InteractivePromise<any> &
209
- Pick<ProtocolRPC, 'namespace' | 'procedure'> & { signal: AbortSignal }
210
-
211
- export type ProtocolBaseClientOptions = {
212
- transport: ProtocolTransport
213
- format: BaseClientFormat
214
- transformer?: ProtocolBaseTransformer
215
- timeout?: number
216
- }
217
-
218
- export type ProtocolBaseClientCallOptions = {
219
- signal?: AbortSignal
220
- timeout: number
221
- }
222
-
223
- export class BaseProtocol<
224
- T extends Record<string, Record<string, any>> = Record<
225
- string,
226
- Record<string, any>
227
- >,
228
- > extends EventEmitter<
229
- {
230
- [N in keyof T]: {
231
- [E in keyof T[N] as `${Extract<N, string>}/${Extract<E, string>}`]: [
232
- payload: T[N][E],
233
- ]
234
- }
235
- }[keyof T]
236
- > {
237
- protected readonly clientStreams: ProtocolClientStreams =
238
- new ProtocolClientStreams()
239
- protected readonly serverStreams: ProtocolServerStreams<ProtocolServerBlobStream> =
240
- new ProtocolServerStreams()
241
- protected readonly rpcStreams: ProtocolServerStreams =
242
- new ProtocolServerStreams()
243
- protected readonly calls = new Map<number, ProtocolClientCall>()
244
- protected callId = 0
245
- protected streamId = 0
246
-
247
- constructor(public readonly format: BaseClientFormat) {
248
- super()
249
- }
250
-
251
- get contentType() {
252
- return this.format.contentType
253
- }
254
-
255
- handleCallResponse(
256
- callId: number,
257
- call: ProtocolClientCall,
258
- response: OneOf<
259
- [{ error: BaseProtocolError }, { result: any; stream?: any }]
260
- >,
261
- transformer: ProtocolBaseTransformer,
262
- ) {
263
- if (response.error) {
264
- call.reject(
265
- new ProtocolError(
266
- response.error.code,
267
- response.error.message,
268
- response.error.data,
269
- ),
270
- )
271
- } else {
272
- try {
273
- const transformed = transformer.decodeRPC(
274
- call.namespace,
275
- call.procedure,
276
- response.result,
277
- )
278
- if (response.stream)
279
- call.resolve({ result: transformed, stream: response.stream })
280
- else call.resolve(transformed)
281
- } catch (error) {
282
- call.reject(
283
- new ProtocolError(
284
- ErrorCode.ClientRequestError,
285
- 'Unable to decode response',
286
- error,
287
- ),
288
- )
289
- }
290
- }
291
- this.calls.delete(callId)
292
- }
293
-
294
- handleRpcResponse(
295
- { callId, error, result, streams }: ProtocolRPCResponse,
296
- transformer: ProtocolBaseTransformer,
297
- stream?: ProtocolServerStream,
298
- ) {
299
- const call = this.calls.get(callId)
300
- if (!call) throw new Error('Call not found')
301
- for (const key in streams) {
302
- const stream = streams[key]
303
- this.serverStreams.add(stream.id, stream)
304
- }
305
- this.handleCallResponse(
306
- callId,
307
- call,
308
- error ? { error } : { result, stream },
309
- transformer,
310
- )
311
- return call
312
- }
313
-
314
- handleRpcStreamResponse(
315
- response: ProtocolRPCResponse,
316
- stream: ProtocolServerStream,
317
- transformer: ProtocolBaseTransformer,
318
- ) {
319
- const call = this.handleRpcResponse(response, transformer, stream)
320
- this.rpcStreams.add(response.callId, stream)
321
- return call
322
- }
323
-
324
- createCall(
325
- namespace: string,
326
- procedure: string,
327
- options: ProtocolBaseClientCallOptions,
328
- ) {
329
- const timeoutSignal = AbortSignal.timeout(options.timeout)
330
- const signal = options.signal
331
- ? AbortSignal.any([options.signal, timeoutSignal])
332
- : timeoutSignal
333
-
334
- const call = Object.assign(createPromise(), {
335
- namespace,
336
- procedure,
337
- signal,
338
- })
339
-
340
- timeoutSignal.addEventListener(
341
- 'abort',
342
- () => {
343
- const error = new ProtocolError(
344
- ErrorCode.RequestTimeout,
345
- 'Request timeout',
346
- )
347
- call.reject(error)
348
- },
349
- { once: true },
350
- )
351
-
352
- return call
353
- }
354
-
355
- createRpc(
356
- namespace: string,
357
- procedure: string,
358
- payload: any,
359
- options: ProtocolBaseClientCallOptions,
360
- transformer: ProtocolBaseTransformer,
361
- ) {
362
- const callId = ++this.callId
363
- const call = this.createCall(namespace, procedure, options)
364
- const { buffer, streams } = this.format.encodeRPC(
365
- {
366
- callId,
367
- namespace,
368
- procedure,
369
- payload: transformer.encodeRPC(namespace, procedure, payload),
370
- },
371
- {
372
- addStream: (blob) => {
373
- const streamId = ++this.streamId
374
- return this.clientStreams.add(blob.source, streamId, blob.metadata)
375
- },
376
- getStream: (id) => {
377
- const stream = this.clientStreams.get(id)
378
- return stream
379
- },
380
- },
381
- )
382
-
383
- this.calls.set(callId, call)
384
-
385
- return { callId, call, streams, buffer }
386
- }
387
-
388
- pushRpcStream(callId: number, chunk: any) {
389
- this.rpcStreams.push(callId, chunk)
390
- }
391
-
392
- endRpcStream(callId: number) {
393
- this.rpcStreams.end(callId)
394
- }
395
-
396
- abortRpcStream(callId: number) {
397
- this.rpcStreams.abort(callId)
398
- }
399
-
400
- removeClientStream(streamId: number) {
401
- this.clientStreams.remove(streamId)
402
- }
403
-
404
- pullClientStream(streamId: number, size: number) {
405
- return this.clientStreams.pull(streamId, size)
406
- }
407
-
408
- endClientStream(streamId: number) {
409
- this.clientStreams.end(streamId)
410
- }
411
-
412
- abortClientStream(streamId: number, error?: Error) {
413
- this.clientStreams.abort(streamId, error)
414
- }
415
-
416
- addServerStream(stream: ProtocolServerBlobStream) {
417
- this.serverStreams.add(stream.id, stream)
418
- }
419
-
420
- removeServerStream(streamId: number) {
421
- this.serverStreams.remove(streamId)
422
- }
423
-
424
- pushServerStream(streamId: number, chunk: ArrayBuffer) {
425
- return this.serverStreams.push(streamId, chunk)
426
- }
427
-
428
- endServerStream(streamId: number) {
429
- this.serverStreams.end(streamId)
430
- }
431
-
432
- abortServerStream(streamId: number, error?: Error) {
433
- this.serverStreams.abort(streamId)
434
- }
435
-
436
- emitEvent(
437
- namespace: string,
438
- event: string,
439
- payload: string,
440
- transformer: ProtocolBaseTransformer,
441
- ) {
442
- const transformed = transformer.decodeEvent(namespace, event, payload)
443
- this.emit(`${namespace}/${event}`, transformed)
444
- }
445
- }
446
-
447
- export class Protocol<
448
- T extends Record<string, Record<string, any>> = Record<
449
- string,
450
- Record<string, any>
451
- >,
452
- > extends BaseProtocol<T> {
453
- handleServerMessage(
454
- buffer: ArrayBuffer,
455
- transport: ProtocolTransport,
456
- transformer: ProtocolBaseTransformer,
457
- ) {
458
- const type = decodeNumber(buffer, 'Uint8')
459
- const messageBuffer = buffer.slice(Uint8Array.BYTES_PER_ELEMENT)
460
- if (type in ServerMessageType) {
461
- const messageType = type as ServerMessageType
462
- if (typeof ServerMessageType[messageType] !== 'undefined') {
463
- this[messageType](messageBuffer, transport, transformer)
464
- } else {
465
- throw new Error(`Unknown message type: ${messageType}`)
466
- }
467
- }
468
- }
469
-
470
- protected [ServerMessageType.Event](
471
- buffer: ArrayBuffer,
472
- transport: ProtocolTransport,
473
- transformer: ProtocolBaseTransformer,
474
- ) {
475
- const [namespace, event, payload] = this.format.decode(buffer)
476
- this.emitEvent(namespace, event, payload, transformer)
477
- }
478
-
479
- protected [ServerMessageType.RpcResponse](
480
- buffer: ArrayBuffer,
481
- transport: ProtocolTransport,
482
- transformer: ProtocolBaseTransformer,
483
- ) {
484
- const response = this.format.decodeRPC(buffer, {
485
- addStream: (id, callId, metadata) => {
486
- return new ProtocolServerBlobStream(id, metadata, {
487
- start: () => {
488
- transport.send(
489
- ClientMessageType.ServerStreamPull,
490
- encodeNumber(id, 'Uint32'),
491
- { callId, streamId: id },
492
- )
493
- },
494
- })
495
- },
496
- getStream: (id) => {
497
- return this.serverStreams.get(id)
498
- },
499
- })
500
- this.handleRpcResponse(response, transformer)
501
- }
502
-
503
- protected [ServerMessageType.RpcStreamResponse](
504
- buffer: ArrayBuffer,
505
- transport: ProtocolTransport,
506
- transformer: ProtocolBaseTransformer,
507
- ) {
508
- const response = this.format.decodeRPC(buffer, {
509
- addStream: (id, callId, metadata) => {
510
- return new ProtocolServerBlobStream(id, metadata, {
511
- start: () => {
512
- transport.send(
513
- ClientMessageType.ServerStreamPull,
514
- encodeNumber(id, 'Uint32'),
515
- { callId, streamId: id },
516
- )
517
- },
518
- })
519
- },
520
- getStream: (id) => {
521
- return this.serverStreams.get(id)
522
- },
523
- })
524
-
525
- const stream = new ProtocolServerStream({
526
- transform: (chunk, controller) => {
527
- const transformed = transformer.decodeRPCChunk(
528
- call.namespace,
529
- call.procedure,
530
- chunk,
531
- )
532
- controller.enqueue(transformed)
533
- },
534
- })
535
-
536
- const call = this.handleRpcStreamResponse(response, stream, transformer)
537
- }
538
-
539
- protected [ServerMessageType.RpcStreamChunk](
540
- buffer: ArrayBuffer,
541
- transport: ProtocolTransport,
542
- transformer: ProtocolBaseTransformer,
543
- ) {
544
- const callId = decodeNumber(buffer, 'Uint32')
545
- const chunk = buffer.slice(Uint32Array.BYTES_PER_ELEMENT)
546
- const payload = this.format.decode(chunk)
547
- this.pushRpcStream(callId, payload)
548
- }
549
-
550
- protected [ServerMessageType.RpcStreamEnd](
551
- buffer: ArrayBuffer,
552
- transport: ProtocolTransport,
553
- transformer: ProtocolBaseTransformer,
554
- ) {
555
- const callId = decodeNumber(buffer, 'Uint32')
556
- this.endRpcStream(callId)
557
- }
558
-
559
- protected [ServerMessageType.RpcStreamAbort](
560
- buffer: ArrayBuffer,
561
- transport: ProtocolTransport,
562
- transformer: ProtocolBaseTransformer,
563
- ) {
564
- const callId = decodeNumber(buffer, 'Uint32')
565
- this.abortRpcStream(callId)
566
- }
567
-
568
- protected [ServerMessageType.ServerStreamPush](
569
- buffer: ArrayBuffer,
570
- transport: ProtocolTransport,
571
- transformer: ProtocolBaseTransformer,
572
- ) {
573
- const streamId = decodeNumber(buffer, 'Uint32')
574
- const chunk = buffer.slice(Uint32Array.BYTES_PER_ELEMENT)
575
- this.pushServerStream(streamId, chunk)
576
- transport.send(
577
- ClientMessageType.ServerStreamPull,
578
- encodeNumber(streamId, 'Uint32'),
579
- { streamId },
580
- )
581
- }
582
-
583
- protected [ServerMessageType.ServerStreamEnd](
584
- buffer: ArrayBuffer,
585
- transport: ProtocolTransport,
586
- transformer: ProtocolBaseTransformer,
587
- ) {
588
- const streamId = decodeNumber(buffer, 'Uint32')
589
- this.endServerStream(streamId)
590
- }
591
-
592
- protected [ServerMessageType.ServerStreamAbort](
593
- buffer: ArrayBuffer,
594
- transport: ProtocolTransport,
595
- transformer: ProtocolBaseTransformer,
596
- ) {
597
- const streamId = decodeNumber(buffer, 'Uint32')
598
- this.abortServerStream(streamId)
599
- }
600
-
601
- protected [ServerMessageType.ClientStreamPull](
602
- buffer: ArrayBuffer,
603
- transport: ProtocolTransport,
604
- transformer: ProtocolBaseTransformer,
605
- ) {
606
- const streamId = decodeNumber(buffer, 'Uint32')
607
- const size = decodeNumber(buffer, 'Uint32', Uint32Array.BYTES_PER_ELEMENT)
608
- this.pullClientStream(streamId, size).then((chunk) => {
609
- if (chunk) {
610
- transport.send(
611
- ClientMessageType.ClientStreamPush,
612
- concat(encodeNumber(streamId, 'Uint32'), chunk),
613
- { streamId },
614
- )
615
- } else {
616
- transport.send(
617
- ClientMessageType.ClientStreamEnd,
618
- encodeNumber(streamId, 'Uint32'),
619
- { streamId },
620
- )
621
- this.endClientStream(streamId)
622
- }
623
- })
624
- }
625
-
626
- protected [ServerMessageType.ClientStreamAbort](
627
- buffer: ArrayBuffer,
628
- transport: ProtocolTransport,
629
- transformer: ProtocolBaseTransformer,
630
- ) {
631
- const streamId = decodeNumber(buffer, 'Uint32')
632
- this.abortClientStream(streamId)
633
- }
634
- }