@nmtjs/protocol 0.15.0-beta.1 → 0.15.0-beta.10
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/client/format.d.ts +26 -0
- package/dist/client/format.js +3 -0
- package/dist/client/format.js.map +1 -0
- package/dist/client/index.d.ts +7 -0
- package/dist/client/index.js +7 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/protocol.d.ts +114 -0
- package/dist/client/protocol.js +25 -0
- package/dist/client/protocol.js.map +1 -0
- package/dist/client/stream.d.ts +49 -0
- package/dist/client/stream.js +190 -0
- package/dist/client/stream.js.map +1 -0
- package/dist/client/versions/v1.d.ts +108 -0
- package/dist/client/versions/v1.js +128 -0
- package/dist/client/versions/v1.js.map +1 -0
- package/dist/common/binary.d.ts +20 -0
- package/dist/common/binary.js +40 -0
- package/dist/common/binary.js.map +1 -0
- package/dist/common/blob.d.ts +29 -0
- package/dist/common/blob.js +67 -0
- package/dist/common/blob.js.map +1 -0
- package/dist/common/constants.d.ts +2 -0
- package/dist/common/constants.js +2 -0
- package/dist/common/constants.js.map +1 -0
- package/dist/common/enums.d.ts +51 -0
- package/dist/common/enums.js +59 -0
- package/dist/common/enums.js.map +1 -0
- package/dist/common/index.d.ts +6 -0
- package/dist/common/index.js +7 -0
- package/dist/common/index.js.map +1 -0
- package/dist/common/types.d.ts +14 -0
- package/dist/common/types.js +2 -0
- package/dist/common/types.js.map +1 -0
- package/dist/common/utils.d.ts +2 -0
- package/dist/common/utils.js +7 -0
- package/dist/common/utils.js.map +1 -0
- package/dist/server/format.d.ts +32 -0
- package/dist/server/format.js +63 -0
- package/dist/server/format.js.map +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +9 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/protocol.d.ts +102 -0
- package/dist/server/protocol.js +25 -0
- package/dist/server/protocol.js.map +1 -0
- package/dist/server/stream.d.ts +15 -0
- package/dist/server/stream.js +42 -0
- package/dist/server/stream.js.map +1 -0
- package/dist/server/types.d.ts +34 -0
- package/dist/server/types.js +2 -0
- package/dist/server/types.js.map +1 -0
- package/dist/server/utils.d.ts +12 -0
- package/dist/server/utils.js +16 -0
- package/dist/server/utils.js.map +1 -0
- package/dist/server/versions/v1.d.ts +77 -0
- package/dist/server/versions/v1.js +119 -0
- package/dist/server/versions/v1.js.map +1 -0
- package/package.json +11 -10
- package/src/client/format.ts +49 -0
- package/src/client/index.ts +8 -0
- package/src/client/protocol.ts +107 -0
- package/src/client/stream.ts +222 -0
- package/src/client/versions/v1.ts +205 -0
- package/src/common/binary.ts +70 -0
- package/src/common/blob.ts +99 -0
- package/src/common/constants.ts +2 -0
- package/src/common/enums.ts +62 -0
- package/src/common/index.ts +6 -0
- package/src/common/types.ts +18 -0
- package/src/common/utils.ts +12 -0
- package/src/server/format.ts +113 -0
- package/src/server/index.ts +10 -0
- package/src/server/protocol.ts +97 -0
- package/src/server/stream.ts +51 -0
- package/src/server/types.ts +42 -0
- package/src/server/utils.ts +22 -0
- package/src/server/versions/v1.ts +198 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export enum ProtocolVersion {
|
|
2
|
+
v1 = 1,
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export enum ClientMessageType {
|
|
6
|
+
Rpc = 10,
|
|
7
|
+
RpcAbort = 11,
|
|
8
|
+
RpcPull = 12,
|
|
9
|
+
|
|
10
|
+
ClientStreamPush = 20,
|
|
11
|
+
ClientStreamEnd = 21,
|
|
12
|
+
ClientStreamAbort = 22,
|
|
13
|
+
|
|
14
|
+
ServerStreamAbort = 33,
|
|
15
|
+
ServerStreamPull = 34,
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export enum ServerMessageType {
|
|
19
|
+
// Event = 1,
|
|
20
|
+
|
|
21
|
+
RpcResponse = 10,
|
|
22
|
+
RpcStreamResponse = 11,
|
|
23
|
+
RpcStreamChunk = 12,
|
|
24
|
+
RpcStreamEnd = 13,
|
|
25
|
+
RpcStreamAbort = 14,
|
|
26
|
+
|
|
27
|
+
ServerStreamPush = 20,
|
|
28
|
+
ServerStreamEnd = 21,
|
|
29
|
+
ServerStreamAbort = 22,
|
|
30
|
+
|
|
31
|
+
ClientStreamAbort = 33,
|
|
32
|
+
ClientStreamPull = 34,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export enum ConnectionType {
|
|
36
|
+
Bidirectional = 'Bidirectional',
|
|
37
|
+
Unidirectional = 'Unidirectional',
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export enum ErrorCode {
|
|
41
|
+
ValidationError = 'ValidationError',
|
|
42
|
+
BadRequest = 'BadRequest',
|
|
43
|
+
NotFound = 'NotFound',
|
|
44
|
+
Forbidden = 'Forbidden',
|
|
45
|
+
Unauthorized = 'Unauthorized',
|
|
46
|
+
InternalServerError = 'InternalServerError',
|
|
47
|
+
NotAcceptable = 'NotAcceptable',
|
|
48
|
+
RequestTimeout = 'RequestTimeout',
|
|
49
|
+
GatewayTimeout = 'GatewayTimeout',
|
|
50
|
+
ServiceUnavailable = 'ServiceUnavailable',
|
|
51
|
+
ClientRequestError = 'ClientRequestError',
|
|
52
|
+
ConnectionError = 'ConnectionError',
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export enum MessageByteLength {
|
|
56
|
+
MessageType = 1,
|
|
57
|
+
MessageError = 1,
|
|
58
|
+
ProcedureLength = 2,
|
|
59
|
+
CallId = 4,
|
|
60
|
+
StreamId = 4,
|
|
61
|
+
ChunkSize = 4,
|
|
62
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ProtocolBlobMetadata } from './blob.ts'
|
|
2
|
+
|
|
3
|
+
type Stream = any
|
|
4
|
+
|
|
5
|
+
export interface BaseProtocolError {
|
|
6
|
+
code: string
|
|
7
|
+
message: string
|
|
8
|
+
data?: any
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type ProtocolRPCPayload = unknown
|
|
12
|
+
export type ProtocolRPCResponse = unknown
|
|
13
|
+
|
|
14
|
+
export type EncodeRPCStreams = Record<number, ProtocolBlobMetadata>
|
|
15
|
+
|
|
16
|
+
export interface DecodeRPCContext<T = Stream> {
|
|
17
|
+
addStream: (id: number, metadata: ProtocolBlobMetadata) => T
|
|
18
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ProtocolBlobInterface } from './blob.ts'
|
|
2
|
+
import { kBlobKey } from './constants.ts'
|
|
3
|
+
|
|
4
|
+
export const isBlobInterface = <T extends ProtocolBlobInterface>(
|
|
5
|
+
value: any,
|
|
6
|
+
): value is T => {
|
|
7
|
+
return (
|
|
8
|
+
value &&
|
|
9
|
+
(typeof value === 'object' || typeof value === 'function') &&
|
|
10
|
+
kBlobKey in value
|
|
11
|
+
)
|
|
12
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import type { Pattern } from '@nmtjs/common'
|
|
2
|
+
import { match } from '@nmtjs/common'
|
|
3
|
+
|
|
4
|
+
import type {
|
|
5
|
+
DecodeRPCContext,
|
|
6
|
+
EncodeRPCStreams,
|
|
7
|
+
ProtocolRPCPayload,
|
|
8
|
+
} from '../common/types.ts'
|
|
9
|
+
import type { ProtocolClientStream } from './stream.ts'
|
|
10
|
+
|
|
11
|
+
export interface BaseServerDecoder {
|
|
12
|
+
accept: Pattern[]
|
|
13
|
+
decode(buffer: ArrayBufferView): unknown
|
|
14
|
+
decodeRPC(
|
|
15
|
+
buffer: ArrayBufferView,
|
|
16
|
+
context: DecodeRPCContext<() => ProtocolClientStream>,
|
|
17
|
+
): ProtocolRPCPayload
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface BaseServerEncoder {
|
|
21
|
+
contentType: string
|
|
22
|
+
encode(data: unknown): ArrayBufferView
|
|
23
|
+
encodeRPC(data: unknown, streams: EncodeRPCStreams): ArrayBufferView
|
|
24
|
+
encodeBlob(streamId: number): unknown
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export abstract class BaseServerFormat
|
|
28
|
+
implements BaseServerDecoder, BaseServerEncoder
|
|
29
|
+
{
|
|
30
|
+
abstract accept: Pattern[]
|
|
31
|
+
abstract contentType: string
|
|
32
|
+
|
|
33
|
+
abstract encode(data: unknown): ArrayBufferView
|
|
34
|
+
abstract encodeRPC(data: unknown, streams: EncodeRPCStreams): ArrayBufferView
|
|
35
|
+
abstract encodeBlob(streamId: number): unknown
|
|
36
|
+
abstract decode(buffer: ArrayBufferView): any
|
|
37
|
+
abstract decodeRPC(
|
|
38
|
+
buffer: ArrayBufferView,
|
|
39
|
+
context: DecodeRPCContext<() => ProtocolClientStream>,
|
|
40
|
+
): ProtocolRPCPayload
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const parseContentTypes = (types: string) => {
|
|
44
|
+
const normalized = types.trim()
|
|
45
|
+
if (normalized === '*/*') return ['*/*']
|
|
46
|
+
return normalized
|
|
47
|
+
.split(',')
|
|
48
|
+
.map((t) => t.trim())
|
|
49
|
+
.map((t) => {
|
|
50
|
+
const [rawType, ...rest] = t.split(';')
|
|
51
|
+
const params = new Map(
|
|
52
|
+
rest.map((p) =>
|
|
53
|
+
p
|
|
54
|
+
.trim()
|
|
55
|
+
.split('=')
|
|
56
|
+
.slice(0, 2)
|
|
57
|
+
.map((part) => part.trim()),
|
|
58
|
+
) as [string, string][],
|
|
59
|
+
)
|
|
60
|
+
return {
|
|
61
|
+
type: rawType.trim(),
|
|
62
|
+
q: params.has('q') ? Number.parseFloat(params.get('q')!) : 1,
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
.sort((a, b) => {
|
|
66
|
+
if (a.type === '*/*') return 1
|
|
67
|
+
if (b.type === '*/*') return -1
|
|
68
|
+
return b.q - a.q
|
|
69
|
+
})
|
|
70
|
+
.map((t) => t.type)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export class ProtocolFormats {
|
|
74
|
+
decoders = new Map<Pattern, BaseServerDecoder>()
|
|
75
|
+
encoders = new Map<Pattern, BaseServerEncoder>()
|
|
76
|
+
|
|
77
|
+
constructor(formats: BaseServerFormat[]) {
|
|
78
|
+
for (const format of formats) {
|
|
79
|
+
this.encoders.set(format.contentType, format)
|
|
80
|
+
for (const acceptType of format.accept) {
|
|
81
|
+
this.decoders.set(acceptType, format)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
supportsDecoder(contentType: string, throwIfUnsupported = false) {
|
|
87
|
+
return this.supports(this.decoders, contentType, throwIfUnsupported)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
supportsEncoder(contentType: string, throwIfUnsupported = false) {
|
|
91
|
+
return this.supports(this.encoders, contentType, throwIfUnsupported)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private supports<T extends BaseServerEncoder | BaseServerDecoder>(
|
|
95
|
+
formats: Map<Pattern, T>,
|
|
96
|
+
contentType: string,
|
|
97
|
+
throwIfUnsupported = false,
|
|
98
|
+
): T | null {
|
|
99
|
+
// TODO: Use node:utils.MIMEType (not implemented yet in Deno and Bun yet)
|
|
100
|
+
const types = parseContentTypes(contentType)
|
|
101
|
+
|
|
102
|
+
for (const type of types) {
|
|
103
|
+
for (const [pattern, format] of formats) {
|
|
104
|
+
if (type === '*/*' || match(type, pattern)) return format
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (throwIfUnsupported)
|
|
109
|
+
throw new Error(`No supported format found: ${contentType}`)
|
|
110
|
+
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './format.ts'
|
|
2
|
+
export * from './protocol.ts'
|
|
3
|
+
export * from './stream.ts'
|
|
4
|
+
export * from './types.ts'
|
|
5
|
+
export * from './utils.ts'
|
|
6
|
+
|
|
7
|
+
import { ProtocolVersion } from '../common/enums.ts'
|
|
8
|
+
import { ProtocolVersion1 } from './versions/v1.ts'
|
|
9
|
+
|
|
10
|
+
export const versions = { [ProtocolVersion.v1]: new ProtocolVersion1() }
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ClientMessageType,
|
|
3
|
+
ProtocolVersion,
|
|
4
|
+
ServerMessageType,
|
|
5
|
+
} from '../common/enums.ts'
|
|
6
|
+
import type { BaseProtocolError, EncodeRPCStreams } from '../common/types.ts'
|
|
7
|
+
import type { MessageContext } from './types.ts'
|
|
8
|
+
import { concat } from '../common/binary.ts'
|
|
9
|
+
|
|
10
|
+
export class ProtocolError extends Error implements BaseProtocolError {
|
|
11
|
+
code: string
|
|
12
|
+
data?: any
|
|
13
|
+
|
|
14
|
+
constructor(code: string, message?: string, data?: any) {
|
|
15
|
+
super(message)
|
|
16
|
+
this.code = code
|
|
17
|
+
this.data = data
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
get message() {
|
|
21
|
+
return `${this.code} ${super.message}`
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
toString() {
|
|
25
|
+
return `${this.code} ${this.message}`
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
toJSON() {
|
|
29
|
+
return { code: this.code, message: this.message, data: this.data }
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export abstract class ProtocolVersionInterface {
|
|
34
|
+
abstract version: ProtocolVersion
|
|
35
|
+
abstract decodeMessage(
|
|
36
|
+
context: MessageContext,
|
|
37
|
+
buffer: ArrayBufferView,
|
|
38
|
+
): {
|
|
39
|
+
[K in keyof ClientMessageTypePayload]: {
|
|
40
|
+
type: K
|
|
41
|
+
} & ClientMessageTypePayload[K]
|
|
42
|
+
}[keyof ClientMessageTypePayload]
|
|
43
|
+
abstract encodeMessage<T extends ServerMessageType = ServerMessageType>(
|
|
44
|
+
context: MessageContext,
|
|
45
|
+
messageType: T,
|
|
46
|
+
payload: ServerMessageTypePayload[T],
|
|
47
|
+
): ArrayBufferView
|
|
48
|
+
|
|
49
|
+
protected encode(
|
|
50
|
+
...chunks: (ArrayBuffer | ArrayBufferView)[]
|
|
51
|
+
): ArrayBufferView {
|
|
52
|
+
return concat(...chunks)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export type ServerMessageTypePayload = {
|
|
57
|
+
// [ServerMessageType.Event]: { event: string; data: any }
|
|
58
|
+
[ServerMessageType.RpcResponse]: {
|
|
59
|
+
callId: number
|
|
60
|
+
result: any
|
|
61
|
+
streams: EncodeRPCStreams
|
|
62
|
+
error: any | null
|
|
63
|
+
}
|
|
64
|
+
[ServerMessageType.RpcStreamAbort]: { callId: number; reason?: string }
|
|
65
|
+
[ServerMessageType.RpcStreamEnd]: { callId: number }
|
|
66
|
+
[ServerMessageType.RpcStreamChunk]: { callId: number; chunk: ArrayBufferView }
|
|
67
|
+
[ServerMessageType.RpcStreamResponse]: { callId: number }
|
|
68
|
+
[ServerMessageType.ClientStreamAbort]: { streamId: number; reason?: string }
|
|
69
|
+
[ServerMessageType.ClientStreamPull]: { streamId: number; size: number }
|
|
70
|
+
[ServerMessageType.ServerStreamAbort]: { streamId: number; reason?: string }
|
|
71
|
+
[ServerMessageType.ServerStreamEnd]: { streamId: number }
|
|
72
|
+
[ServerMessageType.ServerStreamPush]: {
|
|
73
|
+
streamId: number
|
|
74
|
+
chunk: ArrayBufferView
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export type ClientMessageTypePayload = {
|
|
79
|
+
[ClientMessageType.Rpc]: {
|
|
80
|
+
rpc: {
|
|
81
|
+
callId: number
|
|
82
|
+
procedure: string
|
|
83
|
+
payload: unknown
|
|
84
|
+
streams?: EncodeRPCStreams
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
[ClientMessageType.RpcPull]: { callId: number }
|
|
88
|
+
[ClientMessageType.RpcAbort]: { callId: number; reason?: string }
|
|
89
|
+
[ClientMessageType.ClientStreamPush]: {
|
|
90
|
+
streamId: number
|
|
91
|
+
chunk: ArrayBufferView
|
|
92
|
+
}
|
|
93
|
+
[ClientMessageType.ClientStreamEnd]: { streamId: number }
|
|
94
|
+
[ClientMessageType.ClientStreamAbort]: { streamId: number; reason?: string }
|
|
95
|
+
[ClientMessageType.ServerStreamPull]: { streamId: number; size: number }
|
|
96
|
+
[ClientMessageType.ServerStreamAbort]: { streamId: number; reason?: string }
|
|
97
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { ReadableOptions } from 'node:stream'
|
|
2
|
+
import { PassThrough, Readable } from 'node:stream'
|
|
3
|
+
import { ReadableStream } from 'node:stream/web'
|
|
4
|
+
|
|
5
|
+
import type { ProtocolBlob, ProtocolBlobMetadata } from '../common/blob.ts'
|
|
6
|
+
|
|
7
|
+
export class ProtocolClientStream extends PassThrough {
|
|
8
|
+
readonly #read?: ReadableOptions['read']
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
public readonly id: number,
|
|
12
|
+
public readonly metadata: ProtocolBlobMetadata,
|
|
13
|
+
options?: ReadableOptions,
|
|
14
|
+
) {
|
|
15
|
+
const { read, ...rest } = options ?? {}
|
|
16
|
+
super(rest)
|
|
17
|
+
this.#read = read
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
override _read(size: number): void {
|
|
21
|
+
if (this.#read) {
|
|
22
|
+
this.#read.call(this, size)
|
|
23
|
+
}
|
|
24
|
+
super._read(size)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class ProtocolServerStream extends PassThrough {
|
|
29
|
+
public readonly id: number
|
|
30
|
+
public readonly metadata: ProtocolBlobMetadata
|
|
31
|
+
|
|
32
|
+
constructor(id: number, blob: ProtocolBlob) {
|
|
33
|
+
let readable: Readable
|
|
34
|
+
|
|
35
|
+
if (blob.source instanceof Readable) {
|
|
36
|
+
readable = blob.source
|
|
37
|
+
} else if (blob.source instanceof ReadableStream) {
|
|
38
|
+
readable = Readable.fromWeb(blob.source as ReadableStream)
|
|
39
|
+
} else {
|
|
40
|
+
throw new Error('Invalid source type')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
super()
|
|
44
|
+
|
|
45
|
+
this.pause()
|
|
46
|
+
readable.pipe(this)
|
|
47
|
+
|
|
48
|
+
this.id = id
|
|
49
|
+
this.metadata = blob.metadata
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { PlainType } from '@nmtjs/type'
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ProtocolBlobInterface,
|
|
5
|
+
ProtocolBlobMetadata,
|
|
6
|
+
} from '../common/blob.ts'
|
|
7
|
+
import type { kBlobKey } from '../common/constants.ts'
|
|
8
|
+
import type { BaseServerDecoder, BaseServerEncoder } from './format.ts'
|
|
9
|
+
import type { ProtocolVersionInterface } from './protocol.ts'
|
|
10
|
+
import type { ProtocolClientStream } from './stream.ts'
|
|
11
|
+
|
|
12
|
+
export type ClientStreamConsumer = (() => ProtocolClientStream) & {
|
|
13
|
+
readonly [kBlobKey]: any
|
|
14
|
+
readonly metadata: ProtocolBlobMetadata
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type MessageContext = {
|
|
18
|
+
protocol: ProtocolVersionInterface
|
|
19
|
+
connectionId: string
|
|
20
|
+
streamId: () => number
|
|
21
|
+
decoder: BaseServerDecoder
|
|
22
|
+
encoder: BaseServerEncoder
|
|
23
|
+
addClientStream: (options: {
|
|
24
|
+
streamId: number
|
|
25
|
+
metadata: ProtocolBlobMetadata
|
|
26
|
+
callId: number
|
|
27
|
+
}) => ClientStreamConsumer
|
|
28
|
+
transport: {
|
|
29
|
+
send?: (connectionId: string, buffer: ArrayBufferView) => boolean | null
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type ResolveFormatParams = {
|
|
34
|
+
contentType?: string | null
|
|
35
|
+
accept?: string | null
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type InputType<T> = T extends ProtocolBlobInterface
|
|
39
|
+
? ClientStreamConsumer
|
|
40
|
+
: T extends { [PlainType]?: true }
|
|
41
|
+
? { [K in keyof Omit<T, PlainType>]: InputType<T[K]> }
|
|
42
|
+
: T
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProtocolFormats } from './format.ts'
|
|
2
|
+
import type { ResolveFormatParams } from './types.ts'
|
|
3
|
+
|
|
4
|
+
export class UnsupportedFormatError extends Error {}
|
|
5
|
+
|
|
6
|
+
export class UnsupportedContentTypeError extends UnsupportedFormatError {}
|
|
7
|
+
|
|
8
|
+
export class UnsupportedAcceptTypeError extends UnsupportedFormatError {}
|
|
9
|
+
|
|
10
|
+
export const getFormat = (
|
|
11
|
+
format: ProtocolFormats,
|
|
12
|
+
{ accept, contentType }: ResolveFormatParams,
|
|
13
|
+
) => {
|
|
14
|
+
const encoder = contentType ? format.supportsEncoder(contentType) : undefined
|
|
15
|
+
if (!encoder)
|
|
16
|
+
throw new UnsupportedContentTypeError('Unsupported Content type')
|
|
17
|
+
|
|
18
|
+
const decoder = accept ? format.supportsDecoder(accept) : undefined
|
|
19
|
+
if (!decoder) throw new UnsupportedAcceptTypeError('Unsupported Accept type')
|
|
20
|
+
|
|
21
|
+
return { encoder, decoder }
|
|
22
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import type { ServerMessageTypePayload } from '../protocol.ts'
|
|
2
|
+
import type { MessageContext } from '../types.ts'
|
|
3
|
+
import { decodeText, encodeNumber, encodeText } from '../../common/binary.ts'
|
|
4
|
+
import {
|
|
5
|
+
ClientMessageType,
|
|
6
|
+
MessageByteLength,
|
|
7
|
+
ProtocolVersion,
|
|
8
|
+
ServerMessageType,
|
|
9
|
+
} from '../../common/enums.ts'
|
|
10
|
+
import { ProtocolVersionInterface } from '../protocol.ts'
|
|
11
|
+
|
|
12
|
+
export class ProtocolVersion1 extends ProtocolVersionInterface {
|
|
13
|
+
version = ProtocolVersion.v1
|
|
14
|
+
decodeMessage(context: MessageContext, buffer: Buffer) {
|
|
15
|
+
const messageType = buffer.readUint8(0)
|
|
16
|
+
const messagePayload = buffer.subarray(MessageByteLength.MessageType)
|
|
17
|
+
switch (messageType) {
|
|
18
|
+
case ClientMessageType.Rpc: {
|
|
19
|
+
const callId = messagePayload.readUint32LE(0)
|
|
20
|
+
const procedureLength = messagePayload.readUInt16LE(
|
|
21
|
+
MessageByteLength.CallId,
|
|
22
|
+
)
|
|
23
|
+
const procedureOffset =
|
|
24
|
+
MessageByteLength.CallId + MessageByteLength.ProcedureLength
|
|
25
|
+
const procedure = messagePayload.toString(
|
|
26
|
+
'utf-8',
|
|
27
|
+
procedureOffset,
|
|
28
|
+
procedureOffset + procedureLength,
|
|
29
|
+
)
|
|
30
|
+
const formatPayload = messagePayload.subarray(
|
|
31
|
+
procedureOffset + procedureLength,
|
|
32
|
+
)
|
|
33
|
+
const payload = context.decoder.decodeRPC(formatPayload, {
|
|
34
|
+
addStream: (streamId, metadata) => {
|
|
35
|
+
return context.addClientStream({ callId, streamId, metadata })
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
return { type: messageType, rpc: { callId, procedure, payload } }
|
|
40
|
+
}
|
|
41
|
+
case ClientMessageType.RpcPull: {
|
|
42
|
+
const callId = messagePayload.readUInt32LE(0)
|
|
43
|
+
return { type: messageType, callId }
|
|
44
|
+
}
|
|
45
|
+
case ClientMessageType.RpcAbort: {
|
|
46
|
+
const callId = messagePayload.readUInt32LE(0)
|
|
47
|
+
const reasonPayload = messagePayload.subarray(MessageByteLength.CallId)
|
|
48
|
+
const reason =
|
|
49
|
+
reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined
|
|
50
|
+
return { type: messageType, callId, reason }
|
|
51
|
+
}
|
|
52
|
+
case ClientMessageType.ServerStreamAbort: {
|
|
53
|
+
const streamId = messagePayload.readUInt32LE(0)
|
|
54
|
+
const reasonPayload = messagePayload.subarray(
|
|
55
|
+
MessageByteLength.StreamId,
|
|
56
|
+
)
|
|
57
|
+
const reason =
|
|
58
|
+
reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined
|
|
59
|
+
return { type: messageType, streamId, reason }
|
|
60
|
+
}
|
|
61
|
+
case ClientMessageType.ServerStreamPull: {
|
|
62
|
+
const streamId = messagePayload.readUInt32LE(0)
|
|
63
|
+
const size = messagePayload.readUInt32LE(MessageByteLength.StreamId)
|
|
64
|
+
return { type: messageType, streamId, size }
|
|
65
|
+
}
|
|
66
|
+
case ClientMessageType.ClientStreamAbort: {
|
|
67
|
+
const streamId = messagePayload.readUInt32LE(0)
|
|
68
|
+
const reasonPayload = messagePayload.subarray(
|
|
69
|
+
MessageByteLength.StreamId,
|
|
70
|
+
)
|
|
71
|
+
const reason =
|
|
72
|
+
reasonPayload.byteLength > 0 ? decodeText(reasonPayload) : undefined
|
|
73
|
+
return { type: messageType, streamId, reason }
|
|
74
|
+
}
|
|
75
|
+
case ClientMessageType.ClientStreamEnd: {
|
|
76
|
+
return { type: messageType, streamId: messagePayload.readUInt32LE(0) }
|
|
77
|
+
}
|
|
78
|
+
case ClientMessageType.ClientStreamPush: {
|
|
79
|
+
const streamId = messagePayload.readUInt32LE(0)
|
|
80
|
+
const chunk = messagePayload.subarray(MessageByteLength.StreamId)
|
|
81
|
+
return { type: messageType, streamId, chunk }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
default:
|
|
85
|
+
throw new Error(`Unsupported message type: ${messageType}`)
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
encodeMessage<T extends ServerMessageType>(
|
|
90
|
+
context: MessageContext,
|
|
91
|
+
messageType: T,
|
|
92
|
+
payload: ServerMessageTypePayload[T],
|
|
93
|
+
) {
|
|
94
|
+
switch (messageType) {
|
|
95
|
+
// case ServerMessageType.Event: {
|
|
96
|
+
// const { event, data } =
|
|
97
|
+
// payload as ServerMessageTypePayload[ServerMessageType.Event]
|
|
98
|
+
// return this.encode(
|
|
99
|
+
// encodeNumber(messageType, 'Uint8'),
|
|
100
|
+
// context.encoder.encode({ event, data }),
|
|
101
|
+
// )
|
|
102
|
+
// }
|
|
103
|
+
case ServerMessageType.RpcResponse: {
|
|
104
|
+
const { callId, result, streams, error } =
|
|
105
|
+
payload as ServerMessageTypePayload[ServerMessageType.RpcResponse]
|
|
106
|
+
return this.encode(
|
|
107
|
+
encodeNumber(messageType, 'Uint8'),
|
|
108
|
+
encodeNumber(callId, 'Uint32'),
|
|
109
|
+
encodeNumber(error ? 1 : 0, 'Uint8'),
|
|
110
|
+
error
|
|
111
|
+
? context.encoder.encode(error)
|
|
112
|
+
: context.encoder.encodeRPC(result, streams),
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
case ServerMessageType.RpcStreamResponse: {
|
|
116
|
+
const { callId } =
|
|
117
|
+
payload as ServerMessageTypePayload[ServerMessageType.RpcStreamResponse]
|
|
118
|
+
return this.encode(
|
|
119
|
+
encodeNumber(messageType, 'Uint8'),
|
|
120
|
+
encodeNumber(callId, 'Uint32'),
|
|
121
|
+
)
|
|
122
|
+
}
|
|
123
|
+
case ServerMessageType.RpcStreamChunk: {
|
|
124
|
+
const { callId, chunk } =
|
|
125
|
+
payload as ServerMessageTypePayload[ServerMessageType.RpcStreamChunk]
|
|
126
|
+
return this.encode(
|
|
127
|
+
encodeNumber(messageType, 'Uint8'),
|
|
128
|
+
encodeNumber(callId, 'Uint32'),
|
|
129
|
+
chunk,
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
case ServerMessageType.RpcStreamEnd: {
|
|
133
|
+
const { callId } =
|
|
134
|
+
payload as ServerMessageTypePayload[ServerMessageType.RpcStreamEnd]
|
|
135
|
+
return this.encode(
|
|
136
|
+
encodeNumber(messageType, 'Uint8'),
|
|
137
|
+
encodeNumber(callId, 'Uint32'),
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
case ServerMessageType.RpcStreamAbort: {
|
|
141
|
+
const { callId, reason } =
|
|
142
|
+
payload as ServerMessageTypePayload[ServerMessageType.RpcStreamAbort]
|
|
143
|
+
return this.encode(
|
|
144
|
+
encodeNumber(messageType, 'Uint8'),
|
|
145
|
+
encodeNumber(callId, 'Uint32'),
|
|
146
|
+
reason ? encodeText(reason) : Buffer.alloc(0),
|
|
147
|
+
)
|
|
148
|
+
}
|
|
149
|
+
case ServerMessageType.ClientStreamPull: {
|
|
150
|
+
const { size, streamId } =
|
|
151
|
+
payload as ServerMessageTypePayload[ServerMessageType.ClientStreamPull]
|
|
152
|
+
return this.encode(
|
|
153
|
+
encodeNumber(messageType, 'Uint8'),
|
|
154
|
+
encodeNumber(streamId, 'Uint32'),
|
|
155
|
+
encodeNumber(size, 'Uint32'),
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
case ServerMessageType.ClientStreamAbort: {
|
|
159
|
+
const { streamId, reason } =
|
|
160
|
+
payload as ServerMessageTypePayload[ServerMessageType.ClientStreamAbort]
|
|
161
|
+
return this.encode(
|
|
162
|
+
encodeNumber(messageType, 'Uint8'),
|
|
163
|
+
encodeNumber(streamId, 'Uint32'),
|
|
164
|
+
reason ? encodeText(reason) : Buffer.alloc(0),
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
case ServerMessageType.ServerStreamPush: {
|
|
168
|
+
const { streamId, chunk } =
|
|
169
|
+
payload as ServerMessageTypePayload[ServerMessageType.ServerStreamPush]
|
|
170
|
+
return this.encode(
|
|
171
|
+
encodeNumber(messageType, 'Uint8'),
|
|
172
|
+
encodeNumber(streamId, 'Uint32'),
|
|
173
|
+
chunk,
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
case ServerMessageType.ServerStreamEnd: {
|
|
177
|
+
const { streamId } =
|
|
178
|
+
payload as ServerMessageTypePayload[ServerMessageType.ServerStreamEnd]
|
|
179
|
+
return this.encode(
|
|
180
|
+
encodeNumber(messageType, 'Uint8'),
|
|
181
|
+
encodeNumber(streamId, 'Uint32'),
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
case ServerMessageType.ServerStreamAbort: {
|
|
185
|
+
const { streamId, reason } =
|
|
186
|
+
payload as ServerMessageTypePayload[ServerMessageType.ServerStreamAbort]
|
|
187
|
+
return this.encode(
|
|
188
|
+
encodeNumber(messageType, 'Uint8'),
|
|
189
|
+
encodeNumber(streamId, 'Uint32'),
|
|
190
|
+
reason ? encodeText(reason) : Buffer.alloc(0),
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
default:
|
|
195
|
+
throw new Error(`Unsupported message type: ${messageType}`)
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|