starpc 0.23.2 → 0.24.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/e2e/e2e.js +6 -5
- package/dist/rpcstream/rpcstream.d.ts +5 -5
- package/dist/rpcstream/rpcstream.js +1 -1
- package/dist/srpc/broadcast-channel.d.ts +9 -13
- package/dist/srpc/broadcast-channel.js +64 -30
- package/dist/srpc/client.js +11 -4
- package/dist/srpc/common-rpc.d.ts +4 -2
- package/dist/srpc/common-rpc.js +19 -7
- package/dist/srpc/conn.d.ts +16 -12
- package/dist/srpc/conn.js +35 -34
- package/dist/srpc/handler.d.ts +1 -4
- package/dist/srpc/handler.js +1 -67
- package/dist/srpc/index.d.ts +7 -7
- package/dist/srpc/index.js +5 -5
- package/dist/srpc/invoker.d.ts +4 -0
- package/dist/srpc/invoker.js +66 -0
- package/dist/srpc/message-port.d.ts +7 -9
- package/dist/srpc/message-port.js +56 -13
- package/dist/srpc/pushable.d.ts +1 -1
- package/dist/srpc/pushable.js +2 -14
- package/dist/srpc/server-rpc.js +1 -0
- package/dist/srpc/server.d.ts +2 -6
- package/dist/srpc/server.js +4 -17
- package/dist/srpc/stream.d.ts +5 -3
- package/dist/srpc/stream.js +16 -1
- package/dist/srpc/websocket.d.ts +2 -2
- package/dist/srpc/websocket.js +2 -2
- package/e2e/e2e.ts +8 -5
- package/package.json +1 -1
- package/srpc/broadcast-channel.ts +78 -47
- package/srpc/client.ts +10 -3
- package/srpc/common-rpc.go +1 -10
- package/srpc/common-rpc.ts +23 -8
- package/srpc/conn.ts +54 -58
- package/srpc/handler.ts +2 -92
- package/srpc/index.ts +18 -7
- package/srpc/invoker.ts +92 -0
- package/srpc/message-port.ts +71 -21
- package/srpc/open-stream-ctr.ts +2 -2
- package/srpc/pushable.ts +5 -17
- package/srpc/server-rpc.ts +1 -0
- package/srpc/server.ts +5 -37
- package/srpc/stream.ts +32 -7
- package/srpc/websocket.ts +2 -2
- package/dist/srpc/conn-stream.d.ts +0 -8
- package/dist/srpc/conn-stream.js +0 -16
- package/srpc/conn-stream.ts +0 -24
package/dist/e2e/e2e.js
CHANGED
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { pipe } from 'it-pipe';
|
|
2
|
-
import { createHandler, createMux, Server, Client,
|
|
2
|
+
import { createHandler, createMux, Server, Client, StreamConn } from '../srpc';
|
|
3
3
|
import { EchoerDefinition, EchoerServer, runClientTest } from '../echo';
|
|
4
|
-
import { runRpcStreamTest } from '../echo/client-test';
|
|
4
|
+
import { runAbortControllerTest, runRpcStreamTest } from '../echo/client-test';
|
|
5
5
|
async function runRPC() {
|
|
6
6
|
const mux = createMux();
|
|
7
7
|
const server = new Server(mux.lookupMethodFunc);
|
|
8
8
|
const echoer = new EchoerServer(server);
|
|
9
9
|
mux.register(createHandler(EchoerDefinition, echoer));
|
|
10
|
-
const clientConn = new
|
|
11
|
-
const serverConn = new
|
|
10
|
+
const clientConn = new StreamConn();
|
|
11
|
+
const serverConn = new StreamConn(server, { direction: 'inbound' });
|
|
12
12
|
pipe(clientConn, serverConn, clientConn);
|
|
13
13
|
const client = new Client(clientConn.buildOpenStreamFunc());
|
|
14
|
-
await runRpcStreamTest(client);
|
|
15
14
|
await runClientTest(client);
|
|
15
|
+
await runAbortControllerTest(client);
|
|
16
|
+
await runRpcStreamTest(client);
|
|
16
17
|
}
|
|
17
18
|
runRPC()
|
|
18
19
|
.then(() => {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { RpcStreamPacket } from './rpcstream.pb.js';
|
|
2
|
-
import { OpenStreamFunc, Stream } from '../srpc/stream.js';
|
|
3
1
|
import { Pushable } from 'it-pushable';
|
|
4
2
|
import { Source, Sink } from 'it-stream-types';
|
|
3
|
+
import { RpcStreamPacket } from './rpcstream.pb.js';
|
|
4
|
+
import { OpenStreamFunc, PacketStream } from '../srpc/stream.js';
|
|
5
5
|
export type RpcStreamCaller = (request: AsyncIterable<RpcStreamPacket>) => AsyncIterable<RpcStreamPacket>;
|
|
6
|
-
export declare function openRpcStream(componentId: string, caller: RpcStreamCaller, waitAck?: boolean): Promise<
|
|
6
|
+
export declare function openRpcStream(componentId: string, caller: RpcStreamCaller, waitAck?: boolean): Promise<PacketStream>;
|
|
7
7
|
export declare function buildRpcStreamOpenStream(componentId: string, caller: RpcStreamCaller): OpenStreamFunc;
|
|
8
|
-
export type RpcStreamHandler = (stream:
|
|
8
|
+
export type RpcStreamHandler = (stream: PacketStream) => void;
|
|
9
9
|
export type RpcStreamGetter = (componentId: string) => Promise<RpcStreamHandler | null>;
|
|
10
10
|
export declare function handleRpcStream(packetRx: AsyncIterator<RpcStreamPacket>, getter: RpcStreamGetter): AsyncIterable<RpcStreamPacket>;
|
|
11
|
-
export declare class RpcStream implements
|
|
11
|
+
export declare class RpcStream implements PacketStream {
|
|
12
12
|
readonly source: AsyncGenerator<Uint8Array>;
|
|
13
13
|
readonly sink: Sink<Source<Uint8Array>, Promise<void>>;
|
|
14
14
|
private readonly _packetRx;
|
|
@@ -92,7 +92,7 @@ export async function* handleRpcStream(packetRx, getter) {
|
|
|
92
92
|
yield* [packet];
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
|
-
// RpcStream implements the
|
|
95
|
+
// RpcStream implements the PacketStream on top of a RPC call.
|
|
96
96
|
// Note: expects the stream to already have been negotiated.
|
|
97
97
|
export class RpcStream {
|
|
98
98
|
// packetTx writes packets to the remote.
|
|
@@ -1,23 +1,19 @@
|
|
|
1
|
-
import type { Source } from 'it-stream-types';
|
|
2
|
-
import {
|
|
1
|
+
import type { Duplex, Source } from 'it-stream-types';
|
|
2
|
+
import { StreamConn, StreamConnParams } from './conn.js';
|
|
3
3
|
import { Server } from './server.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
readonly readChannel: BroadcastChannel;
|
|
8
|
-
readonly writeChannel: BroadcastChannel;
|
|
4
|
+
export declare class BroadcastChannelDuplex<T> implements Duplex<AsyncGenerator<T>, Source<T>, Promise<void>> {
|
|
5
|
+
readonly read: BroadcastChannel;
|
|
6
|
+
readonly write: BroadcastChannel;
|
|
9
7
|
sink: (source: Source<T>) => Promise<void>;
|
|
10
8
|
source: AsyncGenerator<T>;
|
|
11
|
-
constructor(
|
|
9
|
+
constructor(read: BroadcastChannel, write: BroadcastChannel);
|
|
12
10
|
close(): void;
|
|
13
11
|
private _createSink;
|
|
14
12
|
private _createSource;
|
|
15
13
|
}
|
|
16
14
|
export declare function newBroadcastChannelDuplex<T>(readName: string, writeName: string): BroadcastChannelDuplex<T>;
|
|
17
15
|
export declare class BroadcastChannelConn extends StreamConn {
|
|
18
|
-
|
|
19
|
-
constructor(
|
|
20
|
-
|
|
21
|
-
getWriteChannel(): BroadcastChannel;
|
|
22
|
-
close(): void;
|
|
16
|
+
readonly duplex: BroadcastChannelDuplex<Uint8Array>;
|
|
17
|
+
constructor(duplex: BroadcastChannelDuplex<Uint8Array>, server?: Server, connParams?: StreamConnParams);
|
|
18
|
+
close(err?: Error): void;
|
|
23
19
|
}
|
|
@@ -1,42 +1,67 @@
|
|
|
1
1
|
import { EventIterator } from 'event-iterator';
|
|
2
|
-
import { StreamConn } from './conn
|
|
2
|
+
import { StreamConn } from './conn.js';
|
|
3
|
+
import { combineUint8ArrayListTransform } from './array-list.js';
|
|
4
|
+
import { pipe } from 'it-pipe';
|
|
3
5
|
// BroadcastChannelDuplex is a AsyncIterable wrapper for BroadcastChannel.
|
|
6
|
+
//
|
|
7
|
+
// When the sink is closed, the broadcast channel also be closed.
|
|
8
|
+
// Note: there is no way to know when a BroadcastChannel is closed!
|
|
9
|
+
// You will need an additional keep-alive on top of BroadcastChannelDuplex.
|
|
4
10
|
export class BroadcastChannelDuplex {
|
|
5
|
-
constructor(
|
|
6
|
-
this.
|
|
7
|
-
this.
|
|
11
|
+
constructor(read, write) {
|
|
12
|
+
this.read = read;
|
|
13
|
+
this.write = write;
|
|
8
14
|
this.sink = this._createSink();
|
|
9
15
|
this.source = this._createSource();
|
|
10
16
|
}
|
|
11
|
-
// close closes the
|
|
17
|
+
// close closes the message port.
|
|
12
18
|
close() {
|
|
13
|
-
this.
|
|
14
|
-
this.
|
|
19
|
+
this.write.postMessage(null);
|
|
20
|
+
this.write.close();
|
|
21
|
+
this.read.close();
|
|
15
22
|
}
|
|
16
23
|
// _createSink initializes the sink field.
|
|
17
24
|
_createSink() {
|
|
18
25
|
return async (source) => {
|
|
19
|
-
|
|
20
|
-
|
|
26
|
+
try {
|
|
27
|
+
for await (const msg of source) {
|
|
28
|
+
this.write.postMessage(msg);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
this.close();
|
|
33
|
+
throw err;
|
|
21
34
|
}
|
|
35
|
+
this.close();
|
|
22
36
|
};
|
|
23
37
|
}
|
|
24
38
|
// _createSource initializes the source field.
|
|
25
39
|
async *_createSource() {
|
|
26
40
|
const iterator = new EventIterator((queue) => {
|
|
27
41
|
const messageListener = (ev) => {
|
|
28
|
-
|
|
29
|
-
|
|
42
|
+
const data = ev.data;
|
|
43
|
+
if (data !== null) {
|
|
44
|
+
queue.push(data);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
queue.stop();
|
|
30
48
|
}
|
|
31
49
|
};
|
|
32
|
-
this.
|
|
50
|
+
this.read.addEventListener('message', messageListener);
|
|
33
51
|
return () => {
|
|
34
|
-
this.
|
|
52
|
+
this.read.removeEventListener('message', messageListener);
|
|
35
53
|
};
|
|
36
54
|
});
|
|
37
|
-
|
|
38
|
-
|
|
55
|
+
try {
|
|
56
|
+
for await (const value of iterator) {
|
|
57
|
+
yield value;
|
|
58
|
+
}
|
|
39
59
|
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
this.close();
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
this.close();
|
|
40
65
|
}
|
|
41
66
|
}
|
|
42
67
|
// newBroadcastChannelDuplex constructs a BroadcastChannelDuplex with a channel name.
|
|
@@ -46,22 +71,31 @@ export function newBroadcastChannelDuplex(readName, writeName) {
|
|
|
46
71
|
// BroadcastChannelConn implements a connection with a BroadcastChannel.
|
|
47
72
|
//
|
|
48
73
|
// expects Uint8Array objects over the BroadcastChannel.
|
|
74
|
+
// uses Yamux to mux streams over the port.
|
|
49
75
|
export class BroadcastChannelConn extends StreamConn {
|
|
50
|
-
constructor(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
76
|
+
constructor(duplex, server, connParams) {
|
|
77
|
+
super(server, {
|
|
78
|
+
...connParams,
|
|
79
|
+
yamuxParams: {
|
|
80
|
+
// There is no way to tell when a BroadcastChannel is closed.
|
|
81
|
+
// We will send an undefined object through the BroadcastChannel to indicate closed.
|
|
82
|
+
// We still need a way to detect when the connection is not cleanly terminated.
|
|
83
|
+
// Enable keep-alive to detect this on the other end.
|
|
84
|
+
enableKeepAlive: true,
|
|
85
|
+
keepAliveInterval: 1500,
|
|
86
|
+
...connParams?.yamuxParams,
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
this.duplex = duplex;
|
|
90
|
+
pipe(duplex, this,
|
|
91
|
+
// Uint8ArrayList usually cannot be sent over BroadcastChannel, so we combine to a Uint8Array as part of the pipe.
|
|
92
|
+
combineUint8ArrayListTransform(), duplex)
|
|
93
|
+
.catch((err) => this.close(err))
|
|
94
|
+
.then(() => this.close());
|
|
62
95
|
}
|
|
63
|
-
// close closes the
|
|
64
|
-
close() {
|
|
65
|
-
|
|
96
|
+
// close closes the message port.
|
|
97
|
+
close(err) {
|
|
98
|
+
super.close(err);
|
|
99
|
+
this.duplex.close();
|
|
66
100
|
}
|
|
67
101
|
}
|
package/dist/srpc/client.js
CHANGED
|
@@ -42,7 +42,9 @@ export class Client {
|
|
|
42
42
|
const serverData = pushable({ objectMode: true });
|
|
43
43
|
this.startRpc(service, method, data, abortSignal)
|
|
44
44
|
.then(async (call) => {
|
|
45
|
-
|
|
45
|
+
const result = writeToPushable(call.rpcDataSource, serverData);
|
|
46
|
+
result.finally(() => call.close());
|
|
47
|
+
return result;
|
|
46
48
|
})
|
|
47
49
|
.catch((err) => serverData.end(err));
|
|
48
50
|
return serverData;
|
|
@@ -52,15 +54,17 @@ export class Client {
|
|
|
52
54
|
const serverData = pushable({ objectMode: true });
|
|
53
55
|
this.startRpc(service, method, null, abortSignal)
|
|
54
56
|
.then(async (call) => {
|
|
55
|
-
call.writeCallDataFromSource(data);
|
|
57
|
+
call.writeCallDataFromSource(data).catch((err) => call.close(err));
|
|
56
58
|
try {
|
|
57
59
|
for await (const message of call.rpcDataSource) {
|
|
58
60
|
serverData.push(message);
|
|
59
61
|
}
|
|
60
62
|
serverData.end();
|
|
63
|
+
call.close();
|
|
61
64
|
}
|
|
62
65
|
catch (err) {
|
|
63
|
-
|
|
66
|
+
call.close(err);
|
|
67
|
+
throw err;
|
|
64
68
|
}
|
|
65
69
|
})
|
|
66
70
|
.catch((err) => serverData.end(err));
|
|
@@ -77,9 +81,12 @@ export class Client {
|
|
|
77
81
|
const stream = await openStreamFn();
|
|
78
82
|
const call = new ClientRPC(rpcService, rpcMethod);
|
|
79
83
|
abortSignal?.addEventListener('abort', () => {
|
|
84
|
+
call.writeCallCancel();
|
|
80
85
|
call.close(new Error(ERR_RPC_ABORT));
|
|
81
86
|
});
|
|
82
|
-
pipe(stream, decodePacketSource, call, encodePacketSource, stream)
|
|
87
|
+
pipe(stream, decodePacketSource, call, encodePacketSource, stream)
|
|
88
|
+
.then(() => call.close())
|
|
89
|
+
.catch((err) => call.close(err));
|
|
83
90
|
await call.writeCallStart(data || undefined);
|
|
84
91
|
return call;
|
|
85
92
|
}
|
|
@@ -2,8 +2,8 @@ import type { Sink, Source } from 'it-stream-types';
|
|
|
2
2
|
import type { CallData, CallStart } from './rpcproto.pb.js';
|
|
3
3
|
import { Packet } from './rpcproto.pb.js';
|
|
4
4
|
export declare class CommonRPC {
|
|
5
|
-
sink: Sink<Source<Packet>>;
|
|
6
|
-
source: AsyncIterable<Packet>;
|
|
5
|
+
readonly sink: Sink<Source<Packet>>;
|
|
6
|
+
readonly source: AsyncIterable<Packet>;
|
|
7
7
|
readonly rpcDataSource: AsyncIterable<Uint8Array>;
|
|
8
8
|
private readonly _source;
|
|
9
9
|
private readonly _rpcDataSource;
|
|
@@ -20,6 +20,8 @@ export declare class CommonRPC {
|
|
|
20
20
|
handleCallStart(packet: Partial<CallStart>): Promise<void>;
|
|
21
21
|
protected pushRpcData(data: Uint8Array | undefined, dataIsZero: boolean | undefined): void;
|
|
22
22
|
handleCallData(packet: Partial<CallData>): Promise<void>;
|
|
23
|
+
handleCallCancel(): Promise<void>;
|
|
23
24
|
close(err?: Error): Promise<void>;
|
|
25
|
+
closeWrite(): void;
|
|
24
26
|
private _createSink;
|
|
25
27
|
}
|
package/dist/srpc/common-rpc.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { pushable } from 'it-pushable';
|
|
2
2
|
import { Packet } from './rpcproto.pb.js';
|
|
3
|
+
import { ERR_RPC_ABORT } from './errors.js';
|
|
3
4
|
// CommonRPC is common logic between server and client RPCs.
|
|
4
5
|
export class CommonRPC {
|
|
5
6
|
constructor() {
|
|
@@ -65,6 +66,7 @@ export class CommonRPC {
|
|
|
65
66
|
//
|
|
66
67
|
// note: closes the stream if any error is thrown.
|
|
67
68
|
async handlePacket(packet) {
|
|
69
|
+
// console.log('handlePacket', packet)
|
|
68
70
|
try {
|
|
69
71
|
switch (packet?.body?.$case) {
|
|
70
72
|
case 'callStart':
|
|
@@ -73,6 +75,11 @@ export class CommonRPC {
|
|
|
73
75
|
case 'callData':
|
|
74
76
|
await this.handleCallData(packet.body.callData);
|
|
75
77
|
break;
|
|
78
|
+
case 'callCancel':
|
|
79
|
+
if (packet.body.callCancel) {
|
|
80
|
+
await this.handleCallCancel();
|
|
81
|
+
}
|
|
82
|
+
break;
|
|
76
83
|
}
|
|
77
84
|
}
|
|
78
85
|
catch (err) {
|
|
@@ -114,20 +121,26 @@ export class CommonRPC {
|
|
|
114
121
|
this._rpcDataSource.end();
|
|
115
122
|
}
|
|
116
123
|
}
|
|
124
|
+
// handleCallCancel handles a CallCancel packet.
|
|
125
|
+
async handleCallCancel() {
|
|
126
|
+
this.close(new Error(ERR_RPC_ABORT));
|
|
127
|
+
}
|
|
117
128
|
// close closes the call, optionally with an error.
|
|
118
129
|
async close(err) {
|
|
119
130
|
if (this.closed) {
|
|
120
131
|
return;
|
|
121
132
|
}
|
|
122
133
|
this.closed = true;
|
|
123
|
-
|
|
124
|
-
|
|
134
|
+
// note: don't pass error to _source here.
|
|
135
|
+
if (err) {
|
|
125
136
|
await this.writeCallCancel();
|
|
126
137
|
}
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
138
|
+
this._source.end();
|
|
139
|
+
this._rpcDataSource.end(err);
|
|
140
|
+
}
|
|
141
|
+
// closeWrite closes the call for writing.
|
|
142
|
+
closeWrite() {
|
|
143
|
+
this._source.end();
|
|
131
144
|
}
|
|
132
145
|
// _createSink returns a value for the sink field.
|
|
133
146
|
_createSink() {
|
|
@@ -145,7 +158,6 @@ export class CommonRPC {
|
|
|
145
158
|
await this.handlePacket(msg);
|
|
146
159
|
}
|
|
147
160
|
}
|
|
148
|
-
this._rpcDataSource.end();
|
|
149
161
|
}
|
|
150
162
|
catch (err) {
|
|
151
163
|
this.close(err);
|
package/dist/srpc/conn.d.ts
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import { YamuxMuxerInit } from '@chainsafe/libp2p-yamux';
|
|
2
|
+
import type { Direction, Stream, StreamMuxer, StreamMuxerFactory } from '@libp2p/interface';
|
|
3
|
+
import type { Duplex } from 'it-stream-types';
|
|
3
4
|
import { Uint8ArrayList } from 'uint8arraylist';
|
|
4
|
-
import type
|
|
5
|
+
import { type OpenStreamFunc, type PacketStream } from './stream.js';
|
|
5
6
|
import { Client } from './client.js';
|
|
6
|
-
export interface
|
|
7
|
+
export interface StreamConnParams {
|
|
7
8
|
muxerFactory?: StreamMuxerFactory;
|
|
8
9
|
direction?: Direction;
|
|
10
|
+
yamuxParams?: YamuxMuxerInit;
|
|
9
11
|
}
|
|
10
12
|
export interface StreamHandler {
|
|
11
|
-
handlePacketStream(strm:
|
|
13
|
+
handlePacketStream(strm: PacketStream): void;
|
|
12
14
|
}
|
|
13
|
-
export declare
|
|
14
|
-
|
|
15
|
-
private
|
|
16
|
-
|
|
17
|
-
constructor(server?: StreamHandler, connParams?: ConnParams);
|
|
15
|
+
export declare class StreamConn implements Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> {
|
|
16
|
+
private _muxer;
|
|
17
|
+
private _server?;
|
|
18
|
+
constructor(server?: StreamHandler, connParams?: StreamConnParams);
|
|
18
19
|
get sink(): import("it-stream-types").Sink<AsyncGenerator<Uint8Array | Uint8ArrayList, any, unknown>, unknown>;
|
|
19
20
|
get source(): AsyncGenerator<Uint8Array | Uint8ArrayList, any, unknown>;
|
|
20
21
|
get streams(): Stream[];
|
|
22
|
+
get muxer(): StreamMuxer;
|
|
23
|
+
get server(): StreamHandler | undefined;
|
|
21
24
|
buildClient(): Client;
|
|
22
|
-
openStream(): Promise<
|
|
25
|
+
openStream(): Promise<PacketStream>;
|
|
23
26
|
buildOpenStreamFunc(): OpenStreamFunc;
|
|
24
|
-
|
|
27
|
+
handleIncomingStream(strm: Stream): void;
|
|
28
|
+
close(err?: Error): void;
|
|
25
29
|
}
|
package/dist/srpc/conn.js
CHANGED
|
@@ -1,51 +1,48 @@
|
|
|
1
1
|
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
2
|
-
import { pipe } from 'it-pipe';
|
|
3
|
-
import isPromise from 'is-promise';
|
|
4
|
-
import { pushable } from 'it-pushable';
|
|
5
2
|
import { defaultLogger } from '@libp2p/logger';
|
|
3
|
+
import { streamToPacketStream, } from './stream.js';
|
|
6
4
|
import { Client } from './client.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
export function streamToSRPCStream(stream) {
|
|
13
|
-
const pushSink = pushable({ objectMode: true });
|
|
14
|
-
pipe(pushSink, prependLengthPrefixTransform(), stream.sink);
|
|
15
|
-
return {
|
|
16
|
-
source: pipe(stream, parseLengthPrefixTransform(), combineUint8ArrayListTransform()),
|
|
17
|
-
sink: buildPushableSink(pushSink),
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
// Conn implements a generic connection with a two-way stream.
|
|
5
|
+
// StreamConn implements a generic connection with a two-way stream.
|
|
6
|
+
// The stream is not expected to manage packet boundaries.
|
|
7
|
+
// Packets will be sent with uint32le length prefixes.
|
|
8
|
+
// Uses Yamux to manage streams over the connection.
|
|
9
|
+
//
|
|
21
10
|
// Implements the client by opening streams with the remote.
|
|
22
11
|
// Implements the server by handling incoming streams.
|
|
23
12
|
// If the server is unset, rejects any incoming streams.
|
|
24
|
-
export class
|
|
13
|
+
export class StreamConn {
|
|
25
14
|
constructor(server, connParams) {
|
|
26
15
|
if (server) {
|
|
27
|
-
this.
|
|
16
|
+
this._server = server;
|
|
28
17
|
}
|
|
29
18
|
const muxerFactory = connParams?.muxerFactory ??
|
|
30
|
-
yamux({ enableKeepAlive: false })({
|
|
19
|
+
yamux({ enableKeepAlive: false, ...connParams?.yamuxParams })({
|
|
31
20
|
logger: defaultLogger(),
|
|
32
21
|
});
|
|
33
|
-
this.
|
|
22
|
+
this._muxer = muxerFactory.createStreamMuxer({
|
|
34
23
|
onIncomingStream: this.handleIncomingStream.bind(this),
|
|
35
24
|
direction: connParams?.direction || 'outbound',
|
|
36
25
|
});
|
|
37
26
|
}
|
|
38
27
|
// sink returns the message sink.
|
|
39
28
|
get sink() {
|
|
40
|
-
return this.
|
|
29
|
+
return this._muxer.sink;
|
|
41
30
|
}
|
|
42
31
|
// source returns the outgoing message source.
|
|
43
32
|
get source() {
|
|
44
|
-
return this.
|
|
33
|
+
return this._muxer.source;
|
|
45
34
|
}
|
|
46
35
|
// streams returns the set of all ongoing streams.
|
|
47
36
|
get streams() {
|
|
48
|
-
return this.
|
|
37
|
+
return this._muxer.streams;
|
|
38
|
+
}
|
|
39
|
+
// muxer returns the muxer
|
|
40
|
+
get muxer() {
|
|
41
|
+
return this._muxer;
|
|
42
|
+
}
|
|
43
|
+
// server returns the server, if any.
|
|
44
|
+
get server() {
|
|
45
|
+
return this._server;
|
|
49
46
|
}
|
|
50
47
|
// buildClient builds a new client from the connection.
|
|
51
48
|
buildClient() {
|
|
@@ -53,26 +50,30 @@ export class Conn {
|
|
|
53
50
|
}
|
|
54
51
|
// openStream implements the client open stream function.
|
|
55
52
|
async openStream() {
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (isPromise(streamPromise)) {
|
|
59
|
-
stream = await streamPromise;
|
|
60
|
-
}
|
|
61
|
-
else {
|
|
62
|
-
stream = streamPromise;
|
|
63
|
-
}
|
|
64
|
-
return streamToSRPCStream(stream);
|
|
53
|
+
const strm = await this.muxer.newStream();
|
|
54
|
+
return streamToPacketStream(strm);
|
|
65
55
|
}
|
|
66
56
|
// buildOpenStreamFunc returns openStream bound to this conn.
|
|
67
57
|
buildOpenStreamFunc() {
|
|
68
58
|
return this.openStream.bind(this);
|
|
69
59
|
}
|
|
70
60
|
// handleIncomingStream handles an incoming stream.
|
|
61
|
+
//
|
|
62
|
+
// this is usually called by the muxer when streams arrive.
|
|
71
63
|
handleIncomingStream(strm) {
|
|
72
64
|
const server = this.server;
|
|
73
65
|
if (!server) {
|
|
74
66
|
return strm.abort(new Error('server not implemented'));
|
|
75
67
|
}
|
|
76
|
-
server.handlePacketStream(
|
|
68
|
+
server.handlePacketStream(streamToPacketStream(strm));
|
|
69
|
+
}
|
|
70
|
+
// close closes or aborts the muxer with an optional error.
|
|
71
|
+
close(err) {
|
|
72
|
+
if (err) {
|
|
73
|
+
this.muxer.abort(err);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.muxer.close();
|
|
77
|
+
}
|
|
77
78
|
}
|
|
78
79
|
}
|
package/dist/srpc/handler.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Sink, Source } from 'it-stream-types';
|
|
2
|
-
import { Definition
|
|
2
|
+
import { Definition } from './definition.js';
|
|
3
3
|
export type InvokeFn = (dataSource: Source<Uint8Array>, dataSink: Sink<Source<Uint8Array>>) => Promise<void>;
|
|
4
4
|
export interface Handler {
|
|
5
5
|
getServiceID(): string;
|
|
@@ -17,7 +17,4 @@ export declare class StaticHandler implements Handler {
|
|
|
17
17
|
getMethodIDs(): string[];
|
|
18
18
|
lookupMethod(serviceID: string, methodID: string): Promise<InvokeFn | null>;
|
|
19
19
|
}
|
|
20
|
-
type MethodProto<R, O> = ((request: R) => Promise<O>) | ((request: R) => AsyncIterable<O>) | ((request: AsyncIterable<R>) => Promise<O>) | ((request: AsyncIterable<R>) => AsyncIterable<O>);
|
|
21
|
-
export declare function createInvokeFn<R, O>(methodInfo: MethodDefinition<R, O>, methodProto: MethodProto<R, O>): InvokeFn;
|
|
22
20
|
export declare function createHandler(definition: Definition, impl: any, serviceID?: string): Handler;
|
|
23
|
-
export {};
|
package/dist/srpc/handler.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { pushable } from 'it-pushable';
|
|
3
|
-
import { buildDecodeMessageTransform, buildEncodeMessageTransform, } from './message.js';
|
|
4
|
-
import { writeToPushable } from './pushable.js';
|
|
1
|
+
import { createInvokeFn } from './invoker.js';
|
|
5
2
|
// StaticHandler is a handler with a definition and implementation.
|
|
6
3
|
export class StaticHandler {
|
|
7
4
|
constructor(serviceID, methods) {
|
|
@@ -25,69 +22,6 @@ export class StaticHandler {
|
|
|
25
22
|
return this.methods[methodID] || null;
|
|
26
23
|
}
|
|
27
24
|
}
|
|
28
|
-
// createInvokeFn builds an InvokeFn from a method definition and a function prototype.
|
|
29
|
-
export function createInvokeFn(methodInfo, methodProto) {
|
|
30
|
-
const requestDecode = buildDecodeMessageTransform(methodInfo.requestType);
|
|
31
|
-
return async (dataSource, dataSink) => {
|
|
32
|
-
// responseSink is a Sink for response messages.
|
|
33
|
-
const responseSink = pushable({
|
|
34
|
-
objectMode: true,
|
|
35
|
-
});
|
|
36
|
-
// pipe responseSink to dataSink.
|
|
37
|
-
pipe(responseSink, buildEncodeMessageTransform(methodInfo.responseType), dataSink);
|
|
38
|
-
// requestSource is a Source of decoded request messages.
|
|
39
|
-
const requestSource = pipe(dataSource, requestDecode);
|
|
40
|
-
// build the request argument.
|
|
41
|
-
let requestArg;
|
|
42
|
-
if (methodInfo.requestStream) {
|
|
43
|
-
// use the request source as the argument.
|
|
44
|
-
requestArg = requestSource;
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
// receive a single message for the argument.
|
|
48
|
-
for await (const msg of requestSource) {
|
|
49
|
-
if (msg) {
|
|
50
|
-
requestArg = msg;
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if (!requestArg) {
|
|
56
|
-
throw new Error('request object was empty');
|
|
57
|
-
}
|
|
58
|
-
// Call the implementation.
|
|
59
|
-
try {
|
|
60
|
-
const responseObj = methodProto(requestArg);
|
|
61
|
-
if (!responseObj) {
|
|
62
|
-
throw new Error('return value was undefined');
|
|
63
|
-
}
|
|
64
|
-
if (methodInfo.responseStream) {
|
|
65
|
-
const response = responseObj;
|
|
66
|
-
return writeToPushable(response, responseSink);
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
const responsePromise = responseObj;
|
|
70
|
-
if (!responsePromise.then) {
|
|
71
|
-
throw new Error('expected return value to be a Promise');
|
|
72
|
-
}
|
|
73
|
-
const responseMsg = await responsePromise;
|
|
74
|
-
if (!responseMsg) {
|
|
75
|
-
throw new Error('expected non-empty response object');
|
|
76
|
-
}
|
|
77
|
-
responseSink.push(responseMsg);
|
|
78
|
-
responseSink.end();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
catch (err) {
|
|
82
|
-
let asError = err;
|
|
83
|
-
if (!asError?.message) {
|
|
84
|
-
asError = new Error('error calling implementation: ' + err);
|
|
85
|
-
}
|
|
86
|
-
// mux will return the error to the rpc caller.
|
|
87
|
-
throw asError;
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
25
|
// createHandler creates a handler from a definition and an implementation.
|
|
92
26
|
// if serviceID is not set, uses the fullName of the service as the identifier.
|
|
93
27
|
export function createHandler(definition, impl, serviceID) {
|
package/dist/srpc/index.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
export { ERR_RPC_ABORT, isAbortError, castToError } from './errors.js';
|
|
2
2
|
export { Client } from './client.js';
|
|
3
3
|
export { Server } from './server.js';
|
|
4
|
-
export {
|
|
4
|
+
export { StreamConn, StreamConnParams, StreamHandler } from './conn.js';
|
|
5
5
|
export { WebSocketConn } from './websocket.js';
|
|
6
|
-
export {
|
|
7
|
-
export
|
|
8
|
-
export {
|
|
6
|
+
export type { PacketHandler, OpenStreamFunc, PacketStream, streamToPacketStream, } from './stream.js';
|
|
7
|
+
export { Handler, InvokeFn, MethodMap, StaticHandler, createHandler, } from './handler.js';
|
|
8
|
+
export { MethodProto, createInvokeFn } from './invoker.js';
|
|
9
9
|
export { Packet, CallStart, CallData } from './rpcproto.pb.js';
|
|
10
|
-
export { Mux, StaticMux, createMux } from './mux.js';
|
|
11
|
-
export { BroadcastChannelDuplex,
|
|
12
|
-
export { MessagePortDuplex,
|
|
10
|
+
export { Mux, StaticMux, LookupMethod, createMux } from './mux.js';
|
|
11
|
+
export { BroadcastChannelDuplex, BroadcastChannelConn, newBroadcastChannelDuplex, } from './broadcast-channel.js';
|
|
12
|
+
export { MessagePortDuplex, MessagePortConn, newMessagePortDuplex, } from './message-port.js';
|
|
13
13
|
export { MessageDefinition, DecodeMessageTransform, buildDecodeMessageTransform, EncodeMessageTransform, buildEncodeMessageTransform, memoProto, memoProtoDecode, } from './message.js';
|
|
14
14
|
export { parseLengthPrefixTransform, prependLengthPrefixTransform, decodePacketSource, encodePacketSource, uint32LEDecode, uint32LEEncode, decodeUint32Le, encodeUint32Le, lengthPrefixDecode, lengthPrefixEncode, prependPacketLen, } from './packet.js';
|
|
15
15
|
export { combineUint8ArrayListTransform } from './array-list.js';
|
package/dist/srpc/index.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
export { ERR_RPC_ABORT, isAbortError, castToError } from './errors.js';
|
|
2
2
|
export { Client } from './client.js';
|
|
3
3
|
export { Server } from './server.js';
|
|
4
|
-
export {
|
|
4
|
+
export { StreamConn } from './conn.js';
|
|
5
5
|
export { WebSocketConn } from './websocket.js';
|
|
6
|
-
export {
|
|
7
|
-
export {
|
|
6
|
+
export { StaticHandler, createHandler, } from './handler.js';
|
|
7
|
+
export { createInvokeFn } from './invoker.js';
|
|
8
8
|
export { Packet, CallStart, CallData } from './rpcproto.pb.js';
|
|
9
9
|
export { StaticMux, createMux } from './mux.js';
|
|
10
|
-
export { BroadcastChannelDuplex,
|
|
11
|
-
export { MessagePortDuplex,
|
|
10
|
+
export { BroadcastChannelDuplex, BroadcastChannelConn, newBroadcastChannelDuplex, } from './broadcast-channel.js';
|
|
11
|
+
export { MessagePortDuplex, MessagePortConn, newMessagePortDuplex, } from './message-port.js';
|
|
12
12
|
export { buildDecodeMessageTransform, buildEncodeMessageTransform, memoProto, memoProtoDecode, } from './message.js';
|
|
13
13
|
export { parseLengthPrefixTransform, prependLengthPrefixTransform, decodePacketSource, encodePacketSource, uint32LEDecode, uint32LEEncode, decodeUint32Le, encodeUint32Le, lengthPrefixDecode, lengthPrefixEncode, prependPacketLen, } from './packet.js';
|
|
14
14
|
export { combineUint8ArrayListTransform } from './array-list.js';
|