starpc 0.17.0 → 0.18.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/rpcstream/rpcstream.d.ts +1 -1
- package/dist/rpcstream/rpcstream.js +30 -18
- package/dist/srpc/index.d.ts +1 -1
- package/dist/srpc/index.js +1 -1
- package/dist/srpc/message.d.ts +5 -2
- package/dist/srpc/message.js +28 -6
- package/e2e/e2e_test.go +1 -1
- package/go.mod +1 -1
- package/go.sum +2 -2
- package/package.json +4 -3
- package/srpc/index.ts +2 -0
- package/srpc/message.ts +40 -6
|
@@ -4,7 +4,7 @@ import { Pushable } from 'it-pushable';
|
|
|
4
4
|
import { Source, Sink } from 'it-stream-types';
|
|
5
5
|
import { Uint8ArrayList } from 'uint8arraylist';
|
|
6
6
|
export type RpcStreamCaller = (request: AsyncIterable<RpcStreamPacket>) => AsyncIterable<RpcStreamPacket>;
|
|
7
|
-
export declare function openRpcStream(componentId: string, caller: RpcStreamCaller): Promise<Stream>;
|
|
7
|
+
export declare function openRpcStream(componentId: string, caller: RpcStreamCaller, waitAck?: boolean): Promise<Stream>;
|
|
8
8
|
export declare function buildRpcStreamOpenStream(componentId: string, caller: RpcStreamCaller): OpenStreamFunc;
|
|
9
9
|
export type RpcStreamHandler = (stream: Stream) => void;
|
|
10
10
|
export type RpcStreamGetter = (componentId: string) => Promise<RpcStreamHandler | null>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { pushable } from 'it-pushable';
|
|
2
2
|
// openRpcStream attempts to open a stream over a RPC call.
|
|
3
|
-
// waits for the remote to ack the stream before returning.
|
|
4
|
-
export async function openRpcStream(componentId, caller) {
|
|
3
|
+
// if waitAck is set, waits for the remote to ack the stream before returning.
|
|
4
|
+
export async function openRpcStream(componentId, caller, waitAck) {
|
|
5
5
|
const packetSink = pushable({ objectMode: true });
|
|
6
6
|
const packetSource = caller(packetSink);
|
|
7
7
|
// write the component id
|
|
@@ -11,21 +11,24 @@ export async function openRpcStream(componentId, caller) {
|
|
|
11
11
|
init: { componentId },
|
|
12
12
|
},
|
|
13
13
|
});
|
|
14
|
-
//
|
|
14
|
+
// construct packet stream
|
|
15
15
|
const packetIt = packetSource[Symbol.asyncIterator]();
|
|
16
|
-
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
16
|
+
// wait for ack, if set.
|
|
17
|
+
if (waitAck) {
|
|
18
|
+
const ackPacketIt = await packetIt.next();
|
|
19
|
+
if (ackPacketIt.done) {
|
|
20
|
+
throw new Error(`rpcstream: closed before ack packet`);
|
|
21
|
+
}
|
|
22
|
+
const ackPacket = ackPacketIt.value;
|
|
23
|
+
const ackBody = ackPacket?.body;
|
|
24
|
+
if (!ackBody || ackBody.$case !== 'ack') {
|
|
25
|
+
const msgType = ackBody?.$case || 'none';
|
|
26
|
+
throw new Error(`rpcstream: expected ack packet but got ${msgType}`);
|
|
27
|
+
}
|
|
28
|
+
const errStr = ackBody.ack?.error;
|
|
29
|
+
if (errStr) {
|
|
30
|
+
throw new Error(`rpcstream: remote: ${errStr}`);
|
|
31
|
+
}
|
|
29
32
|
}
|
|
30
33
|
// build & return the data stream
|
|
31
34
|
return new RpcStream(packetSink, packetIt); // packetSource)
|
|
@@ -136,10 +139,19 @@ export class RpcStream {
|
|
|
136
139
|
}
|
|
137
140
|
const value = msgIt.value;
|
|
138
141
|
const body = value?.body;
|
|
139
|
-
if (!body
|
|
142
|
+
if (!body) {
|
|
140
143
|
continue;
|
|
141
144
|
}
|
|
142
|
-
|
|
145
|
+
switch (body.$case) {
|
|
146
|
+
case 'ack':
|
|
147
|
+
if (body.ack?.error?.length) {
|
|
148
|
+
throw new Error(body.ack.error);
|
|
149
|
+
}
|
|
150
|
+
break;
|
|
151
|
+
case 'data':
|
|
152
|
+
yield* [body.data];
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
143
155
|
}
|
|
144
156
|
})();
|
|
145
157
|
}
|
package/dist/srpc/index.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export { Packet, CallStart, CallData } from './rpcproto.pb.js';
|
|
|
8
8
|
export { Mux, StaticMux, createMux } from './mux.js';
|
|
9
9
|
export { BroadcastChannelDuplex, newBroadcastChannelDuplex, BroadcastChannelConn, } from './broadcast-channel.js';
|
|
10
10
|
export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
|
|
11
|
-
export { MessageDefinition, DecodeMessageTransform, buildDecodeMessageTransform, EncodeMessageTransform, buildEncodeMessageTransform, } from './message.js';
|
|
11
|
+
export { MessageDefinition, DecodeMessageTransform, buildDecodeMessageTransform, EncodeMessageTransform, buildEncodeMessageTransform, memoProto, memoProtoDecode, } from './message.js';
|
|
12
12
|
export { ValueCtr } from './value-ctr.js';
|
|
13
13
|
export { OpenStreamCtr } from './open-stream-ctr.js';
|
|
14
14
|
export { writeToPushable } from './pushable';
|
package/dist/srpc/index.js
CHANGED
|
@@ -7,7 +7,7 @@ export { Packet, CallStart, CallData } from './rpcproto.pb.js';
|
|
|
7
7
|
export { StaticMux, createMux } from './mux.js';
|
|
8
8
|
export { BroadcastChannelDuplex, newBroadcastChannelDuplex, BroadcastChannelConn, } from './broadcast-channel.js';
|
|
9
9
|
export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
|
|
10
|
-
export { buildDecodeMessageTransform, buildEncodeMessageTransform, } from './message.js';
|
|
10
|
+
export { buildDecodeMessageTransform, buildEncodeMessageTransform, memoProto, memoProtoDecode, } from './message.js';
|
|
11
11
|
export { ValueCtr } from './value-ctr.js';
|
|
12
12
|
export { OpenStreamCtr } from './open-stream-ctr.js';
|
|
13
13
|
export { writeToPushable } from './pushable';
|
package/dist/srpc/message.d.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import * as pbjs from 'protobufjs/minimal';
|
|
2
2
|
import type { Source } from 'it-stream-types';
|
|
3
3
|
export interface MessageDefinition<T> {
|
|
4
|
+
create(msg?: Partial<T>): T;
|
|
4
5
|
encode(message: T, writer?: pbjs.Writer): pbjs.Writer;
|
|
5
6
|
decode(input: pbjs.Reader | Uint8Array, length?: number): T;
|
|
6
7
|
}
|
|
8
|
+
export declare function memoProto<T>(def: MessageDefinition<T>): (msg: Partial<T>) => Uint8Array;
|
|
9
|
+
export declare function memoProtoDecode<T>(def: MessageDefinition<T>): (msg: Uint8Array) => T;
|
|
7
10
|
export type DecodeMessageTransform<T> = (source: Source<Uint8Array | Uint8Array[]>) => AsyncIterable<T>;
|
|
8
|
-
export declare function buildDecodeMessageTransform<T>(def: MessageDefinition<T
|
|
11
|
+
export declare function buildDecodeMessageTransform<T>(def: MessageDefinition<T>, memoize?: boolean): DecodeMessageTransform<T>;
|
|
9
12
|
export type EncodeMessageTransform<T> = (source: Source<T | T[]>) => AsyncIterable<Uint8Array>;
|
|
10
|
-
export declare function buildEncodeMessageTransform<T>(def: MessageDefinition<T
|
|
13
|
+
export declare function buildEncodeMessageTransform<T>(def: MessageDefinition<T>, memoize?: boolean): EncodeMessageTransform<T>;
|
package/dist/srpc/message.js
CHANGED
|
@@ -1,31 +1,53 @@
|
|
|
1
|
+
import memoize from 'memoize-one';
|
|
2
|
+
// memoProto returns a function that encodes the message and caches the result.
|
|
3
|
+
export function memoProto(def) {
|
|
4
|
+
return memoize((msg) => {
|
|
5
|
+
return def.encode(def.create(msg)).finish();
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
// memoProtoDecode returns a function that decodes the message and caches the result.
|
|
9
|
+
export function memoProtoDecode(def) {
|
|
10
|
+
return memoize((msg) => {
|
|
11
|
+
return def.decode(msg);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
1
14
|
// buildDecodeMessageTransform builds a source of decoded messages.
|
|
2
|
-
|
|
15
|
+
//
|
|
16
|
+
// set memoize if you expect to repeatedly see the same message.
|
|
17
|
+
export function buildDecodeMessageTransform(def, memoize) {
|
|
18
|
+
const decode = memoize ? memoProtoDecode(def) : def.decode.bind(def);
|
|
3
19
|
// decodeMessageSource unmarshals and async yields encoded Messages.
|
|
4
20
|
return async function* decodeMessageSource(source) {
|
|
5
21
|
for await (const pkt of source) {
|
|
6
22
|
if (Array.isArray(pkt)) {
|
|
7
23
|
for (const p of pkt) {
|
|
8
|
-
yield* [
|
|
24
|
+
yield* [decode(p)];
|
|
9
25
|
}
|
|
10
26
|
}
|
|
11
27
|
else {
|
|
12
|
-
yield* [
|
|
28
|
+
yield* [decode(pkt)];
|
|
13
29
|
}
|
|
14
30
|
}
|
|
15
31
|
};
|
|
16
32
|
}
|
|
17
33
|
// buildEncodeMessageTransform builds a source of decoded messages.
|
|
18
|
-
|
|
34
|
+
// set memoize if you expect to repeatedly encode the same message.
|
|
35
|
+
export function buildEncodeMessageTransform(def, memoize) {
|
|
36
|
+
const encode = memoize
|
|
37
|
+
? memoProto(def)
|
|
38
|
+
: (msg) => {
|
|
39
|
+
return def.encode(msg).finish();
|
|
40
|
+
};
|
|
19
41
|
// encodeMessageSource encodes messages to byte arrays.
|
|
20
42
|
return async function* encodeMessageSource(source) {
|
|
21
43
|
for await (const pkt of source) {
|
|
22
44
|
if (Array.isArray(pkt)) {
|
|
23
45
|
for (const p of pkt) {
|
|
24
|
-
yield* [
|
|
46
|
+
yield* [encode(p)];
|
|
25
47
|
}
|
|
26
48
|
}
|
|
27
49
|
else {
|
|
28
|
-
yield* [
|
|
50
|
+
yield* [encode(pkt)];
|
|
29
51
|
}
|
|
30
52
|
}
|
|
31
53
|
};
|
package/e2e/e2e_test.go
CHANGED
|
@@ -227,7 +227,7 @@ func TestE2E_RpcStream(t *testing.T) {
|
|
|
227
227
|
RunE2E(t, func(client echo.SRPCEchoerClient) error {
|
|
228
228
|
openStreamFn := rpcstream.NewRpcStreamOpenStream(func(ctx context.Context) (rpcstream.RpcStream, error) {
|
|
229
229
|
return client.RpcStream(ctx)
|
|
230
|
-
}, "test")
|
|
230
|
+
}, "test", false)
|
|
231
231
|
proxiedClient := srpc.NewClient(openStreamFn)
|
|
232
232
|
proxiedSvc := echo.NewSRPCEchoerClient(proxiedClient)
|
|
233
233
|
|
package/go.mod
CHANGED
|
@@ -9,7 +9,7 @@ require (
|
|
|
9
9
|
)
|
|
10
10
|
|
|
11
11
|
require (
|
|
12
|
-
github.com/aperturerobotics/util
|
|
12
|
+
github.com/aperturerobotics/util v1.0.3-0.20230130061818-dab10b56858b
|
|
13
13
|
github.com/libp2p/go-libp2p v0.24.2
|
|
14
14
|
github.com/libp2p/go-yamux/v4 v4.0.1-0.20220919134236-1c09f2ab3ec1
|
|
15
15
|
github.com/sirupsen/logrus v1.9.0
|
package/go.sum
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
github.com/aperturerobotics/util
|
|
2
|
-
github.com/aperturerobotics/util
|
|
1
|
+
github.com/aperturerobotics/util v1.0.3-0.20230130061818-dab10b56858b h1:E+riBVCoYAZL2ATHTlDZYZRZj/6FPb+UGHVTQdU03UU=
|
|
2
|
+
github.com/aperturerobotics/util v1.0.3-0.20230130061818-dab10b56858b/go.mod h1:gFLdglkzXYEWP/iYrOGeJEKzfjarlMDPrLNLgwDHC1o=
|
|
3
3
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
4
4
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
|
5
5
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Streaming protobuf RPC service protocol over any two-way channel.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"build": "rimraf ./dist && tsc --project tsconfig.build.json --outDir ./dist/",
|
|
37
37
|
"check": "npm run typecheck",
|
|
38
38
|
"typecheck": "tsc --noEmit",
|
|
39
|
-
"deps": "depcheck --ignores 'bufferutil,utf-8-validate,ts-proto,@aperturerobotics/ts-common'",
|
|
39
|
+
"deps": "depcheck --ignores 'bufferutil,utf-8-validate,ts-proto,rimraf,@aperturerobotics/ts-common'",
|
|
40
40
|
"codegen": "npm run gen",
|
|
41
41
|
"ci": "npm run build && npm run lint:js && npm run lint:go",
|
|
42
42
|
"format": "prettier --write './{srpc,echo,e2e,integration,rpcstream}/**/(*.ts|*.tsx|*.html|*.css)'",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"prettier": "^2.8.3",
|
|
72
72
|
"rimraf": "^4.1.0",
|
|
73
73
|
"ts-proto": "^1.138.0",
|
|
74
|
-
"typescript": "^4.9.
|
|
74
|
+
"typescript": "^4.9.5",
|
|
75
75
|
"utf-8-validate": "^6.0.0"
|
|
76
76
|
},
|
|
77
77
|
"dependencies": {
|
|
@@ -88,6 +88,7 @@
|
|
|
88
88
|
"it-stream-types": "^1.0.5",
|
|
89
89
|
"it-ws": "^5.0.6",
|
|
90
90
|
"long": "^5.2.1",
|
|
91
|
+
"memoize-one": "^6.0.0",
|
|
91
92
|
"patch-package": "^6.5.1",
|
|
92
93
|
"protobufjs": "^7.1.2",
|
|
93
94
|
"uint8arraylist": "^2.4.3",
|
package/srpc/index.ts
CHANGED
package/srpc/message.ts
CHANGED
|
@@ -1,23 +1,49 @@
|
|
|
1
1
|
import * as pbjs from 'protobufjs/minimal'
|
|
2
2
|
import type { Source } from 'it-stream-types'
|
|
3
|
+
import memoize from 'memoize-one'
|
|
3
4
|
|
|
4
5
|
// MessageDefinition represents a ts-proto message definition.
|
|
5
6
|
export interface MessageDefinition<T> {
|
|
7
|
+
// create creates a full message from a partial and/or no message.
|
|
8
|
+
create(msg?: Partial<T>): T
|
|
6
9
|
// encode encodes the message and returns writer.
|
|
7
10
|
encode(message: T, writer?: pbjs.Writer): pbjs.Writer
|
|
8
11
|
// decode decodes the message from the reader
|
|
9
12
|
decode(input: pbjs.Reader | Uint8Array, length?: number): T
|
|
10
13
|
}
|
|
11
14
|
|
|
15
|
+
// memoProto returns a function that encodes the message and caches the result.
|
|
16
|
+
export function memoProto<T>(
|
|
17
|
+
def: MessageDefinition<T>
|
|
18
|
+
): (msg: Partial<T>) => Uint8Array {
|
|
19
|
+
return memoize((msg: Partial<T>): Uint8Array => {
|
|
20
|
+
return def.encode(def.create(msg)).finish()
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// memoProtoDecode returns a function that decodes the message and caches the result.
|
|
25
|
+
export function memoProtoDecode<T>(
|
|
26
|
+
def: MessageDefinition<T>
|
|
27
|
+
): (msg: Uint8Array) => T {
|
|
28
|
+
return memoize((msg: Uint8Array): T => {
|
|
29
|
+
return def.decode(msg)
|
|
30
|
+
})
|
|
31
|
+
}
|
|
32
|
+
|
|
12
33
|
// DecodeMessageTransform decodes messages to objects.
|
|
13
34
|
export type DecodeMessageTransform<T> = (
|
|
14
35
|
source: Source<Uint8Array | Uint8Array[]>
|
|
15
36
|
) => AsyncIterable<T>
|
|
16
37
|
|
|
17
38
|
// buildDecodeMessageTransform builds a source of decoded messages.
|
|
39
|
+
//
|
|
40
|
+
// set memoize if you expect to repeatedly see the same message.
|
|
18
41
|
export function buildDecodeMessageTransform<T>(
|
|
19
|
-
def: MessageDefinition<T
|
|
42
|
+
def: MessageDefinition<T>,
|
|
43
|
+
memoize?: boolean
|
|
20
44
|
): DecodeMessageTransform<T> {
|
|
45
|
+
const decode = memoize ? memoProtoDecode(def) : def.decode.bind(def)
|
|
46
|
+
|
|
21
47
|
// decodeMessageSource unmarshals and async yields encoded Messages.
|
|
22
48
|
return async function* decodeMessageSource(
|
|
23
49
|
source: Source<Uint8Array | Uint8Array[]>
|
|
@@ -25,10 +51,10 @@ export function buildDecodeMessageTransform<T>(
|
|
|
25
51
|
for await (const pkt of source) {
|
|
26
52
|
if (Array.isArray(pkt)) {
|
|
27
53
|
for (const p of pkt) {
|
|
28
|
-
yield* [
|
|
54
|
+
yield* [decode(p)]
|
|
29
55
|
}
|
|
30
56
|
} else {
|
|
31
|
-
yield* [
|
|
57
|
+
yield* [decode(pkt)]
|
|
32
58
|
}
|
|
33
59
|
}
|
|
34
60
|
}
|
|
@@ -40,9 +66,17 @@ export type EncodeMessageTransform<T> = (
|
|
|
40
66
|
) => AsyncIterable<Uint8Array>
|
|
41
67
|
|
|
42
68
|
// buildEncodeMessageTransform builds a source of decoded messages.
|
|
69
|
+
// set memoize if you expect to repeatedly encode the same message.
|
|
43
70
|
export function buildEncodeMessageTransform<T>(
|
|
44
|
-
def: MessageDefinition<T
|
|
71
|
+
def: MessageDefinition<T>,
|
|
72
|
+
memoize?: boolean
|
|
45
73
|
): EncodeMessageTransform<T> {
|
|
74
|
+
const encode = memoize
|
|
75
|
+
? memoProto(def)
|
|
76
|
+
: (msg: T): Uint8Array => {
|
|
77
|
+
return def.encode(msg).finish()
|
|
78
|
+
}
|
|
79
|
+
|
|
46
80
|
// encodeMessageSource encodes messages to byte arrays.
|
|
47
81
|
return async function* encodeMessageSource(
|
|
48
82
|
source: Source<T | T[]>
|
|
@@ -50,10 +84,10 @@ export function buildEncodeMessageTransform<T>(
|
|
|
50
84
|
for await (const pkt of source) {
|
|
51
85
|
if (Array.isArray(pkt)) {
|
|
52
86
|
for (const p of pkt) {
|
|
53
|
-
yield* [
|
|
87
|
+
yield* [encode(p)]
|
|
54
88
|
}
|
|
55
89
|
} else {
|
|
56
|
-
yield* [
|
|
90
|
+
yield* [encode(pkt)]
|
|
57
91
|
}
|
|
58
92
|
}
|
|
59
93
|
}
|