starpc 0.4.0 → 0.4.3
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/Makefile +20 -12
- package/README.md +4 -0
- package/dist/rpcstream/rpcstream.d.ts +19 -0
- package/dist/rpcstream/rpcstream.js +96 -0
- package/dist/rpcstream/rpcstream.pb.d.ts +85 -0
- package/dist/rpcstream/rpcstream.pb.js +160 -0
- package/dist/srpc/conn.d.ts +1 -1
- package/dist/srpc/index.d.ts +1 -0
- package/dist/srpc/index.js +1 -0
- package/dist/srpc/observable-source.d.ts +9 -0
- package/dist/srpc/observable-source.js +25 -0
- package/dist/srpc/server.d.ts +3 -3
- package/dist/srpc/server.js +6 -5
- package/package.json +4 -3
- package/srpc/client.ts +3 -1
- package/srpc/conn.ts +1 -1
- package/srpc/index.ts +1 -1
- package/srpc/observable-source.ts +40 -0
- package/srpc/server.ts +7 -6
- package/e2e/debug.test +0 -0
package/Makefile
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
# https://github.com/aperturerobotics/protobuf-project
|
|
2
|
+
|
|
1
3
|
PROTOWRAP=hack/bin/protowrap
|
|
2
4
|
PROTOC_GEN_GO=hack/bin/protoc-gen-go
|
|
5
|
+
PROTOC_GEN_GO_STARPC=hack/bin/protoc-gen-go-starpc
|
|
3
6
|
PROTOC_GEN_VTPROTO=hack/bin/protoc-gen-go-vtproto
|
|
4
|
-
PROTOC_GEN_STARPC=hack/bin/protoc-gen-go-starpc
|
|
5
7
|
GOIMPORTS=hack/bin/goimports
|
|
6
8
|
GOLANGCI_LINT=hack/bin/golangci-lint
|
|
7
9
|
GO_MOD_OUTDATED=hack/bin/go-mod-outdated
|
|
8
|
-
export GO111MODULE=on
|
|
9
10
|
GOLIST=go list -f "{{ .Dir }}" -m
|
|
10
11
|
|
|
12
|
+
export GO111MODULE=on
|
|
13
|
+
undefine GOARCH
|
|
14
|
+
undefine GOOS
|
|
15
|
+
|
|
11
16
|
all:
|
|
12
17
|
|
|
13
18
|
vendor:
|
|
@@ -17,19 +22,19 @@ $(PROTOC_GEN_GO):
|
|
|
17
22
|
cd ./hack; \
|
|
18
23
|
go build -v \
|
|
19
24
|
-o ./bin/protoc-gen-go \
|
|
20
|
-
|
|
25
|
+
google.golang.org/protobuf/cmd/protoc-gen-go
|
|
21
26
|
|
|
22
|
-
$(
|
|
27
|
+
$(PROTOC_GEN_GO_STARPC):
|
|
23
28
|
cd ./hack; \
|
|
24
29
|
go build -v \
|
|
25
|
-
-o ./bin/protoc-gen-go-
|
|
26
|
-
github.com/
|
|
30
|
+
-o ./bin/protoc-gen-go-starpc \
|
|
31
|
+
github.com/aperturerobotics/starpc/cmd/protoc-gen-go-starpc
|
|
27
32
|
|
|
28
|
-
$(
|
|
33
|
+
$(PROTOC_GEN_VTPROTO):
|
|
29
34
|
cd ./hack; \
|
|
30
35
|
go build -v \
|
|
31
|
-
-o ./bin/protoc-gen-go-
|
|
32
|
-
github.com/
|
|
36
|
+
-o ./bin/protoc-gen-go-vtproto \
|
|
37
|
+
github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto
|
|
33
38
|
|
|
34
39
|
$(GOIMPORTS):
|
|
35
40
|
cd ./hack; \
|
|
@@ -56,7 +61,7 @@ $(GO_MOD_OUTDATED):
|
|
|
56
61
|
github.com/psampaz/go-mod-outdated
|
|
57
62
|
|
|
58
63
|
.PHONY: gengo
|
|
59
|
-
gengo: $(GOIMPORTS) $(PROTOWRAP) $(PROTOC_GEN_GO) $(
|
|
64
|
+
gengo: $(GOIMPORTS) $(PROTOWRAP) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_STARPC) $(PROTOC_GEN_VTPROTO)
|
|
60
65
|
go mod vendor
|
|
61
66
|
shopt -s globstar; \
|
|
62
67
|
set -eo pipefail; \
|
|
@@ -120,15 +125,19 @@ genproto: gengo gents
|
|
|
120
125
|
.PHONY: gen
|
|
121
126
|
gen: genproto
|
|
122
127
|
|
|
128
|
+
.PHONY: outdated
|
|
123
129
|
outdated: $(GO_MOD_OUTDATED)
|
|
124
130
|
go list -mod=mod -u -m -json all | $(GO_MOD_OUTDATED) -update -direct
|
|
125
131
|
|
|
132
|
+
.PHONY: list
|
|
126
133
|
list: $(GO_MOD_OUTDATED)
|
|
127
134
|
go list -mod=mod -u -m -json all | $(GO_MOD_OUTDATED)
|
|
128
135
|
|
|
136
|
+
.PHONY: lint
|
|
129
137
|
lint: $(GOLANGCI_LINT)
|
|
130
138
|
$(GOLANGCI_LINT) run
|
|
131
139
|
|
|
140
|
+
.PHONY: fix
|
|
132
141
|
fix: $(GOLANGCI_LINT)
|
|
133
142
|
$(GOLANGCI_LINT) run --fix
|
|
134
143
|
|
|
@@ -138,5 +147,4 @@ test:
|
|
|
138
147
|
|
|
139
148
|
.PHONY: integration
|
|
140
149
|
integration: node_modules vendor
|
|
141
|
-
cd ./integration &&
|
|
142
|
-
bash ./integration.bash
|
|
150
|
+
cd ./integration && bash ./integration.bash
|
package/README.md
CHANGED
|
@@ -13,6 +13,10 @@ Can use any Stream multiplexer: defaults to [libp2p-mplex] over a WebSocket.
|
|
|
13
13
|
|
|
14
14
|
[libp2p-mplex]: https://github.com/libp2p/js-libp2p-mplex
|
|
15
15
|
|
|
16
|
+
[rpcstream] supports sub-streams for per-component sub-services.
|
|
17
|
+
|
|
18
|
+
[rpcstream]: ./rpcstream
|
|
19
|
+
|
|
16
20
|
# Usage
|
|
17
21
|
|
|
18
22
|
Starting with the [protobuf-project] repository on the "starpc" branch.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { Packet } from './rpcstream.pb.js';
|
|
3
|
+
import { Server } from '../srpc/server.js';
|
|
4
|
+
import { OpenStreamFunc, Stream } from '../srpc/stream.js';
|
|
5
|
+
import { Pushable } from 'it-pushable';
|
|
6
|
+
import { Source, Sink } from 'it-stream-types';
|
|
7
|
+
export declare type RpcStreamCaller = (request: Observable<Packet>) => Observable<Packet>;
|
|
8
|
+
export declare function buildRpcStreamOpenStream(componentId: string, caller: RpcStreamCaller): OpenStreamFunc;
|
|
9
|
+
export declare type RpcStreamGetter = (componentId: string) => Promise<Server>;
|
|
10
|
+
export declare function handleRpcStream(stream: Observable<Packet>, getter: RpcStreamGetter): AsyncIterable<Packet>;
|
|
11
|
+
export declare class RpcStream implements Stream {
|
|
12
|
+
readonly source: Source<Uint8Array>;
|
|
13
|
+
readonly sink: Sink<Uint8Array>;
|
|
14
|
+
private readonly _packetSink;
|
|
15
|
+
private readonly _source;
|
|
16
|
+
constructor(packetSink: Pushable<Packet>, packetSource: Observable<Packet>);
|
|
17
|
+
private _createSink;
|
|
18
|
+
private _subscribePacketSource;
|
|
19
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { from as obsFrom } from 'rxjs';
|
|
2
|
+
import { pushable } from 'it-pushable';
|
|
3
|
+
// buildRpcStreamOpenStream builds a OpenStream func with a RpcStream.
|
|
4
|
+
export function buildRpcStreamOpenStream(componentId, caller) {
|
|
5
|
+
return async () => {
|
|
6
|
+
const packetSink = pushable({ objectMode: true });
|
|
7
|
+
const packetObs = obsFrom(packetSink);
|
|
8
|
+
const packetSource = caller(packetObs);
|
|
9
|
+
// write the component id
|
|
10
|
+
packetSink.push({
|
|
11
|
+
body: {
|
|
12
|
+
$case: 'init',
|
|
13
|
+
init: { componentId },
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
// build & return the stream
|
|
17
|
+
return new RpcStream(packetSink, packetSource);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// handleRpcStream handles an incoming RPC stream (remote is the initiator).
|
|
21
|
+
export async function* handleRpcStream(stream, getter) {
|
|
22
|
+
// read the component id
|
|
23
|
+
const initPromise = new Promise((resolve, reject) => {
|
|
24
|
+
const subscription = stream.subscribe({
|
|
25
|
+
next(value) {
|
|
26
|
+
resolve(value);
|
|
27
|
+
subscription.unsubscribe();
|
|
28
|
+
},
|
|
29
|
+
error(err) {
|
|
30
|
+
reject(err);
|
|
31
|
+
},
|
|
32
|
+
complete() {
|
|
33
|
+
reject(new Error('no packet received'));
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
// read the init packet
|
|
38
|
+
const initPacket = await initPromise;
|
|
39
|
+
if (initPacket?.body?.$case !== 'init') {
|
|
40
|
+
throw new Error('expected init packet');
|
|
41
|
+
}
|
|
42
|
+
// lookup the server for the component id.
|
|
43
|
+
const server = await getter(initPacket.body.init.componentId);
|
|
44
|
+
// build the outgoing packet sink & the packet source
|
|
45
|
+
const packetSink = pushable({ objectMode: true });
|
|
46
|
+
// handle the stream
|
|
47
|
+
const rpcStream = new RpcStream(packetSink, stream);
|
|
48
|
+
server.handleDuplex(rpcStream);
|
|
49
|
+
// process packets
|
|
50
|
+
for await (const packet of packetSink) {
|
|
51
|
+
yield* [packet];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
// RpcStream implements the Stream on top of a RPC call.
|
|
55
|
+
export class RpcStream {
|
|
56
|
+
constructor(packetSink, packetSource) {
|
|
57
|
+
this._packetSink = packetSink;
|
|
58
|
+
this.sink = this._createSink();
|
|
59
|
+
const source = pushable({ objectMode: true });
|
|
60
|
+
this.source = source;
|
|
61
|
+
this._source = source;
|
|
62
|
+
this._subscribePacketSource(packetSource);
|
|
63
|
+
}
|
|
64
|
+
// _createSink initializes the sink field.
|
|
65
|
+
_createSink() {
|
|
66
|
+
return async (source) => {
|
|
67
|
+
try {
|
|
68
|
+
for await (const msg of source) {
|
|
69
|
+
this._packetSink.push({
|
|
70
|
+
body: { $case: 'data', data: msg }
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
this._packetSink.end();
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
this._packetSink.end(err);
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// _subscribePacketSource starts the subscription to the response data.
|
|
81
|
+
_subscribePacketSource(packetSource) {
|
|
82
|
+
packetSource.subscribe({
|
|
83
|
+
next: (value) => {
|
|
84
|
+
if (value?.body?.$case === 'data') {
|
|
85
|
+
this._source.push(value.body.data);
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
error: (err) => {
|
|
89
|
+
this._source.end(err);
|
|
90
|
+
},
|
|
91
|
+
complete: () => {
|
|
92
|
+
this._source.end();
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Long from 'long';
|
|
2
|
+
import * as _m0 from 'protobufjs/minimal';
|
|
3
|
+
export declare const protobufPackage = "rpcstream";
|
|
4
|
+
/** Packet is a packet encapsulating data for a RPC stream. */
|
|
5
|
+
export interface Packet {
|
|
6
|
+
body?: {
|
|
7
|
+
$case: 'init';
|
|
8
|
+
init: RpcStreamInit;
|
|
9
|
+
} | {
|
|
10
|
+
$case: 'data';
|
|
11
|
+
data: Uint8Array;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** RpcStreamInit is the first message in a RPC stream. */
|
|
15
|
+
export interface RpcStreamInit {
|
|
16
|
+
/** ComponentId is the identifier of the component making the request. */
|
|
17
|
+
componentId: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const Packet: {
|
|
20
|
+
encode(message: Packet, writer?: _m0.Writer): _m0.Writer;
|
|
21
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): Packet;
|
|
22
|
+
fromJSON(object: any): Packet;
|
|
23
|
+
toJSON(message: Packet): unknown;
|
|
24
|
+
fromPartial<I extends {
|
|
25
|
+
body?: ({
|
|
26
|
+
init?: {
|
|
27
|
+
componentId?: string | undefined;
|
|
28
|
+
} | undefined;
|
|
29
|
+
} & {
|
|
30
|
+
$case: "init";
|
|
31
|
+
}) | ({
|
|
32
|
+
data?: Uint8Array | undefined;
|
|
33
|
+
} & {
|
|
34
|
+
$case: "data";
|
|
35
|
+
}) | undefined;
|
|
36
|
+
} & {
|
|
37
|
+
body?: ({
|
|
38
|
+
init?: {
|
|
39
|
+
componentId?: string | undefined;
|
|
40
|
+
} | undefined;
|
|
41
|
+
} & {
|
|
42
|
+
$case: "init";
|
|
43
|
+
} & {
|
|
44
|
+
init?: ({
|
|
45
|
+
componentId?: string | undefined;
|
|
46
|
+
} & {
|
|
47
|
+
componentId?: string | undefined;
|
|
48
|
+
} & Record<Exclude<keyof I["body"]["init"], "componentId">, never>) | undefined;
|
|
49
|
+
$case: "init";
|
|
50
|
+
} & Record<Exclude<keyof I["body"], "$case" | "init">, never>) | ({
|
|
51
|
+
data?: Uint8Array | undefined;
|
|
52
|
+
} & {
|
|
53
|
+
$case: "data";
|
|
54
|
+
} & {
|
|
55
|
+
data?: Uint8Array | undefined;
|
|
56
|
+
$case: "data";
|
|
57
|
+
} & Record<Exclude<keyof I["body"], "$case" | "data">, never>) | undefined;
|
|
58
|
+
} & Record<Exclude<keyof I, "body">, never>>(object: I): Packet;
|
|
59
|
+
};
|
|
60
|
+
export declare const RpcStreamInit: {
|
|
61
|
+
encode(message: RpcStreamInit, writer?: _m0.Writer): _m0.Writer;
|
|
62
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): RpcStreamInit;
|
|
63
|
+
fromJSON(object: any): RpcStreamInit;
|
|
64
|
+
toJSON(message: RpcStreamInit): unknown;
|
|
65
|
+
fromPartial<I extends {
|
|
66
|
+
componentId?: string | undefined;
|
|
67
|
+
} & {
|
|
68
|
+
componentId?: string | undefined;
|
|
69
|
+
} & Record<Exclude<keyof I, "componentId">, never>>(object: I): RpcStreamInit;
|
|
70
|
+
};
|
|
71
|
+
declare type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
72
|
+
export declare type DeepPartial<T> = T extends Builtin ? T : T extends Long ? string | number | Long : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {
|
|
73
|
+
$case: string;
|
|
74
|
+
} ? {
|
|
75
|
+
[K in keyof Omit<T, '$case'>]?: DeepPartial<T[K]>;
|
|
76
|
+
} & {
|
|
77
|
+
$case: T['$case'];
|
|
78
|
+
} : T extends {} ? {
|
|
79
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
80
|
+
} : Partial<T>;
|
|
81
|
+
declare type KeysOfUnion<T> = T extends T ? keyof T : never;
|
|
82
|
+
export declare type Exact<P, I extends P> = P extends Builtin ? P : P & {
|
|
83
|
+
[K in keyof P]: Exact<P[K], I[K]>;
|
|
84
|
+
} & Record<Exclude<keyof I, KeysOfUnion<P>>, never>;
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import Long from 'long';
|
|
3
|
+
import * as _m0 from 'protobufjs/minimal';
|
|
4
|
+
export const protobufPackage = 'rpcstream';
|
|
5
|
+
function createBasePacket() {
|
|
6
|
+
return { body: undefined };
|
|
7
|
+
}
|
|
8
|
+
export const Packet = {
|
|
9
|
+
encode(message, writer = _m0.Writer.create()) {
|
|
10
|
+
if (message.body?.$case === 'init') {
|
|
11
|
+
RpcStreamInit.encode(message.body.init, writer.uint32(10).fork()).ldelim();
|
|
12
|
+
}
|
|
13
|
+
if (message.body?.$case === 'data') {
|
|
14
|
+
writer.uint32(18).bytes(message.body.data);
|
|
15
|
+
}
|
|
16
|
+
return writer;
|
|
17
|
+
},
|
|
18
|
+
decode(input, length) {
|
|
19
|
+
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
|
20
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
21
|
+
const message = createBasePacket();
|
|
22
|
+
while (reader.pos < end) {
|
|
23
|
+
const tag = reader.uint32();
|
|
24
|
+
switch (tag >>> 3) {
|
|
25
|
+
case 1:
|
|
26
|
+
message.body = {
|
|
27
|
+
$case: 'init',
|
|
28
|
+
init: RpcStreamInit.decode(reader, reader.uint32()),
|
|
29
|
+
};
|
|
30
|
+
break;
|
|
31
|
+
case 2:
|
|
32
|
+
message.body = { $case: 'data', data: reader.bytes() };
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
reader.skipType(tag & 7);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return message;
|
|
40
|
+
},
|
|
41
|
+
fromJSON(object) {
|
|
42
|
+
return {
|
|
43
|
+
body: isSet(object.init)
|
|
44
|
+
? { $case: 'init', init: RpcStreamInit.fromJSON(object.init) }
|
|
45
|
+
: isSet(object.data)
|
|
46
|
+
? { $case: 'data', data: bytesFromBase64(object.data) }
|
|
47
|
+
: undefined,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
toJSON(message) {
|
|
51
|
+
const obj = {};
|
|
52
|
+
message.body?.$case === 'init' &&
|
|
53
|
+
(obj.init = message.body?.init
|
|
54
|
+
? RpcStreamInit.toJSON(message.body?.init)
|
|
55
|
+
: undefined);
|
|
56
|
+
message.body?.$case === 'data' &&
|
|
57
|
+
(obj.data =
|
|
58
|
+
message.body?.data !== undefined
|
|
59
|
+
? base64FromBytes(message.body?.data)
|
|
60
|
+
: undefined);
|
|
61
|
+
return obj;
|
|
62
|
+
},
|
|
63
|
+
fromPartial(object) {
|
|
64
|
+
const message = createBasePacket();
|
|
65
|
+
if (object.body?.$case === 'init' &&
|
|
66
|
+
object.body?.init !== undefined &&
|
|
67
|
+
object.body?.init !== null) {
|
|
68
|
+
message.body = {
|
|
69
|
+
$case: 'init',
|
|
70
|
+
init: RpcStreamInit.fromPartial(object.body.init),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (object.body?.$case === 'data' &&
|
|
74
|
+
object.body?.data !== undefined &&
|
|
75
|
+
object.body?.data !== null) {
|
|
76
|
+
message.body = { $case: 'data', data: object.body.data };
|
|
77
|
+
}
|
|
78
|
+
return message;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
function createBaseRpcStreamInit() {
|
|
82
|
+
return { componentId: '' };
|
|
83
|
+
}
|
|
84
|
+
export const RpcStreamInit = {
|
|
85
|
+
encode(message, writer = _m0.Writer.create()) {
|
|
86
|
+
if (message.componentId !== '') {
|
|
87
|
+
writer.uint32(10).string(message.componentId);
|
|
88
|
+
}
|
|
89
|
+
return writer;
|
|
90
|
+
},
|
|
91
|
+
decode(input, length) {
|
|
92
|
+
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
|
93
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
94
|
+
const message = createBaseRpcStreamInit();
|
|
95
|
+
while (reader.pos < end) {
|
|
96
|
+
const tag = reader.uint32();
|
|
97
|
+
switch (tag >>> 3) {
|
|
98
|
+
case 1:
|
|
99
|
+
message.componentId = reader.string();
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
reader.skipType(tag & 7);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return message;
|
|
107
|
+
},
|
|
108
|
+
fromJSON(object) {
|
|
109
|
+
return {
|
|
110
|
+
componentId: isSet(object.componentId) ? String(object.componentId) : '',
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
toJSON(message) {
|
|
114
|
+
const obj = {};
|
|
115
|
+
message.componentId !== undefined && (obj.componentId = message.componentId);
|
|
116
|
+
return obj;
|
|
117
|
+
},
|
|
118
|
+
fromPartial(object) {
|
|
119
|
+
const message = createBaseRpcStreamInit();
|
|
120
|
+
message.componentId = object.componentId ?? '';
|
|
121
|
+
return message;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
var globalThis = (() => {
|
|
125
|
+
if (typeof globalThis !== 'undefined')
|
|
126
|
+
return globalThis;
|
|
127
|
+
if (typeof self !== 'undefined')
|
|
128
|
+
return self;
|
|
129
|
+
if (typeof window !== 'undefined')
|
|
130
|
+
return window;
|
|
131
|
+
if (typeof global !== 'undefined')
|
|
132
|
+
return global;
|
|
133
|
+
throw 'Unable to locate global object';
|
|
134
|
+
})();
|
|
135
|
+
const atob = globalThis.atob ||
|
|
136
|
+
((b64) => globalThis.Buffer.from(b64, 'base64').toString('binary'));
|
|
137
|
+
function bytesFromBase64(b64) {
|
|
138
|
+
const bin = atob(b64);
|
|
139
|
+
const arr = new Uint8Array(bin.length);
|
|
140
|
+
for (let i = 0; i < bin.length; ++i) {
|
|
141
|
+
arr[i] = bin.charCodeAt(i);
|
|
142
|
+
}
|
|
143
|
+
return arr;
|
|
144
|
+
}
|
|
145
|
+
const btoa = globalThis.btoa ||
|
|
146
|
+
((bin) => globalThis.Buffer.from(bin, 'binary').toString('base64'));
|
|
147
|
+
function base64FromBytes(arr) {
|
|
148
|
+
const bin = [];
|
|
149
|
+
arr.forEach((byte) => {
|
|
150
|
+
bin.push(String.fromCharCode(byte));
|
|
151
|
+
});
|
|
152
|
+
return btoa(bin.join(''));
|
|
153
|
+
}
|
|
154
|
+
if (_m0.util.Long !== Long) {
|
|
155
|
+
_m0.util.Long = Long;
|
|
156
|
+
_m0.configure();
|
|
157
|
+
}
|
|
158
|
+
function isSet(value) {
|
|
159
|
+
return value !== null && value !== undefined;
|
|
160
|
+
}
|
package/dist/srpc/conn.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface ConnParams {
|
|
|
7
7
|
muxerFactory?: StreamMuxerFactory;
|
|
8
8
|
}
|
|
9
9
|
export interface StreamHandler {
|
|
10
|
-
handleStream(strm: Duplex<Uint8Array>):
|
|
10
|
+
handleStream(strm: Duplex<Uint8Array>): void;
|
|
11
11
|
}
|
|
12
12
|
export declare class Conn implements Duplex<Uint8Array> {
|
|
13
13
|
private muxer;
|
package/dist/srpc/index.d.ts
CHANGED
|
@@ -6,3 +6,4 @@ export { Handler, InvokeFn, createHandler, createInvokeFn } from './handler.js';
|
|
|
6
6
|
export { Mux, createMux } from './mux.js';
|
|
7
7
|
export { BroadcastChannelIterable, newBroadcastChannelIterable, BroadcastChannelConn, } from './broadcast-channel.js';
|
|
8
8
|
export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
|
|
9
|
+
export { ObservableSource } from './observable-source.js';
|
package/dist/srpc/index.js
CHANGED
|
@@ -5,3 +5,4 @@ export { createHandler, createInvokeFn } from './handler.js';
|
|
|
5
5
|
export { createMux } from './mux.js';
|
|
6
6
|
export { BroadcastChannelIterable, newBroadcastChannelIterable, BroadcastChannelConn, } from './broadcast-channel.js';
|
|
7
7
|
export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
|
|
8
|
+
export { ObservableSource } from './observable-source.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Source } from 'it-stream-types';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
export declare class ObservableSource<T> {
|
|
4
|
+
readonly source: Source<T>;
|
|
5
|
+
private readonly _source;
|
|
6
|
+
private readonly subscription;
|
|
7
|
+
constructor(observable: Observable<T>);
|
|
8
|
+
close(err?: Error): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { pushable } from 'it-pushable';
|
|
2
|
+
// ObservableSource wraps an Observable into a Source.
|
|
3
|
+
export class ObservableSource {
|
|
4
|
+
constructor(observable) {
|
|
5
|
+
const source = pushable({ objectMode: true });
|
|
6
|
+
this.source = source;
|
|
7
|
+
this._source = source;
|
|
8
|
+
this.subscription = observable.subscribe({
|
|
9
|
+
next: (value) => {
|
|
10
|
+
this._source.push(value);
|
|
11
|
+
},
|
|
12
|
+
error: (err) => {
|
|
13
|
+
this._source.end(err);
|
|
14
|
+
},
|
|
15
|
+
complete: () => {
|
|
16
|
+
this._source.end();
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
// close closes the subscription.
|
|
21
|
+
close(err) {
|
|
22
|
+
this._source.end(err);
|
|
23
|
+
this.subscription.unsubscribe();
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/srpc/server.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare class Server implements StreamHandler {
|
|
|
8
8
|
private mux;
|
|
9
9
|
constructor(mux: Mux);
|
|
10
10
|
startRpc(): ServerRPC;
|
|
11
|
-
handleStream(stream: Stream):
|
|
12
|
-
handleDuplex(stream: Duplex<Uint8Array>):
|
|
13
|
-
handlePacketStream(stream: Duplex<Packet>):
|
|
11
|
+
handleStream(stream: Stream): ServerRPC;
|
|
12
|
+
handleDuplex(stream: Duplex<Uint8Array>): ServerRPC;
|
|
13
|
+
handlePacketStream(stream: Duplex<Packet>): ServerRPC;
|
|
14
14
|
}
|
package/dist/srpc/server.js
CHANGED
|
@@ -12,18 +12,19 @@ export class Server {
|
|
|
12
12
|
return new ServerRPC(this.mux);
|
|
13
13
|
}
|
|
14
14
|
// handleStream handles an incoming Uint8Array message duplex.
|
|
15
|
-
// closes the stream when the rpc completes.
|
|
16
15
|
handleStream(stream) {
|
|
17
16
|
return this.handleDuplex(stream);
|
|
18
17
|
}
|
|
19
18
|
// handleDuplex handles an incoming message duplex.
|
|
20
|
-
|
|
19
|
+
handleDuplex(stream) {
|
|
21
20
|
const rpc = this.startRpc();
|
|
22
|
-
|
|
21
|
+
pipe(stream, parseLengthPrefixTransform(), decodePacketSource, rpc, encodePacketSource, prependLengthPrefixTransform(), stream);
|
|
22
|
+
return rpc;
|
|
23
23
|
}
|
|
24
24
|
// handlePacketStream handles an incoming Packet duplex.
|
|
25
|
-
|
|
25
|
+
handlePacketStream(stream) {
|
|
26
26
|
const rpc = this.startRpc();
|
|
27
|
-
|
|
27
|
+
pipe(stream, rpc, stream);
|
|
28
|
+
return rpc;
|
|
28
29
|
}
|
|
29
30
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starpc",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.3",
|
|
4
4
|
"description": "Streaming protobuf RPC service protocol over any two-way channel.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"Makefile",
|
|
23
23
|
"dist/echo",
|
|
24
24
|
"dist/srpc",
|
|
25
|
+
"dist/rpcstream",
|
|
25
26
|
"e2e",
|
|
26
27
|
"echo",
|
|
27
28
|
"go.mod",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"deps": "depcheck",
|
|
40
41
|
"codegen": "npm run gen",
|
|
41
42
|
"ci": "npm run build && npm run lint:js && npm run lint:go",
|
|
42
|
-
"format": "prettier --write './{srpc,echo,e2e,integration}/**/(*.ts|*.tsx|*.html|*.css)'",
|
|
43
|
+
"format": "prettier --write './{srpc,echo,e2e,integration,rpcstream}/**/(*.ts|*.tsx|*.html|*.css)'",
|
|
43
44
|
"gen": "make genproto",
|
|
44
45
|
"test": "npm run test:js && npm run test:go",
|
|
45
46
|
"test:go": "make test",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"integration": "npm run test:integration",
|
|
49
50
|
"lint": "npm run lint:go && npm run lint:js",
|
|
50
51
|
"lint:go": "make lint",
|
|
51
|
-
"lint:js": "eslint -c .eslintrc.js --ext .ts ./{srpc,echo}/**/*.ts",
|
|
52
|
+
"lint:js": "eslint -c .eslintrc.js --ext .ts ./{srpc,echo,rpcstream}/**/*.ts",
|
|
52
53
|
"prepare": "patch-package",
|
|
53
54
|
"precommit": "npm run format"
|
|
54
55
|
},
|
package/srpc/client.ts
CHANGED
|
@@ -39,7 +39,9 @@ export class Client implements TsProtoRpc {
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
// setOpenStreamFn updates the openStreamFn for the Client.
|
|
42
|
-
public setOpenStreamFn(
|
|
42
|
+
public setOpenStreamFn(
|
|
43
|
+
openStreamFn?: OpenStreamFunc
|
|
44
|
+
): Promise<OpenStreamFunc> {
|
|
43
45
|
if (this._openStreamFn) {
|
|
44
46
|
if (openStreamFn) {
|
|
45
47
|
this._openStreamFn(openStreamFn)
|
package/srpc/conn.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface ConnParams {
|
|
|
19
19
|
// Implemented by Server.
|
|
20
20
|
export interface StreamHandler {
|
|
21
21
|
// handleStream handles an incoming stream.
|
|
22
|
-
handleStream(strm: Duplex<Uint8Array>):
|
|
22
|
+
handleStream(strm: Duplex<Uint8Array>): void
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// Conn implements a generic connection with a two-way stream.
|
package/srpc/index.ts
CHANGED
|
@@ -9,9 +9,9 @@ export {
|
|
|
9
9
|
newBroadcastChannelIterable,
|
|
10
10
|
BroadcastChannelConn,
|
|
11
11
|
} from './broadcast-channel.js'
|
|
12
|
-
|
|
13
12
|
export {
|
|
14
13
|
MessagePortIterable,
|
|
15
14
|
newMessagePortIterable,
|
|
16
15
|
MessagePortConn,
|
|
17
16
|
} from './message-port.js'
|
|
17
|
+
export { ObservableSource } from './observable-source.js'
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Source } from 'it-stream-types'
|
|
2
|
+
import { pushable, Pushable } from 'it-pushable'
|
|
3
|
+
import { Observable, Subscription } from 'rxjs'
|
|
4
|
+
|
|
5
|
+
// ObservableSource wraps an Observable into a Source.
|
|
6
|
+
export class ObservableSource<T> {
|
|
7
|
+
// source is the source for observable objects.
|
|
8
|
+
public readonly source: Source<T>
|
|
9
|
+
// _source emits incoming data to the source.
|
|
10
|
+
private readonly _source: {
|
|
11
|
+
push: (val: T) => void
|
|
12
|
+
end: (err?: Error) => void
|
|
13
|
+
}
|
|
14
|
+
// subscription is the observable subscription
|
|
15
|
+
private readonly subscription: Subscription
|
|
16
|
+
|
|
17
|
+
constructor(observable: Observable<T>) {
|
|
18
|
+
const source: Pushable<T> = pushable({ objectMode: true })
|
|
19
|
+
this.source = source
|
|
20
|
+
this._source = source
|
|
21
|
+
|
|
22
|
+
this.subscription = observable.subscribe({
|
|
23
|
+
next: (value: T) => {
|
|
24
|
+
this._source.push(value)
|
|
25
|
+
},
|
|
26
|
+
error: (err) => {
|
|
27
|
+
this._source.end(err)
|
|
28
|
+
},
|
|
29
|
+
complete: () => {
|
|
30
|
+
this._source.end()
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// close closes the subscription.
|
|
36
|
+
public close(err?: Error) {
|
|
37
|
+
this._source.end(err)
|
|
38
|
+
this.subscription.unsubscribe()
|
|
39
|
+
}
|
|
40
|
+
}
|
package/srpc/server.ts
CHANGED
|
@@ -29,15 +29,14 @@ export class Server implements StreamHandler {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// handleStream handles an incoming Uint8Array message duplex.
|
|
32
|
-
|
|
33
|
-
public handleStream(stream: Stream): Promise<void> {
|
|
32
|
+
public handleStream(stream: Stream): ServerRPC {
|
|
34
33
|
return this.handleDuplex(stream)
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
// handleDuplex handles an incoming message duplex.
|
|
38
|
-
public
|
|
37
|
+
public handleDuplex(stream: Duplex<Uint8Array>): ServerRPC {
|
|
39
38
|
const rpc = this.startRpc()
|
|
40
|
-
|
|
39
|
+
pipe(
|
|
41
40
|
stream,
|
|
42
41
|
parseLengthPrefixTransform(),
|
|
43
42
|
decodePacketSource,
|
|
@@ -46,11 +45,13 @@ export class Server implements StreamHandler {
|
|
|
46
45
|
prependLengthPrefixTransform(),
|
|
47
46
|
stream
|
|
48
47
|
)
|
|
48
|
+
return rpc
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// handlePacketStream handles an incoming Packet duplex.
|
|
52
|
-
public
|
|
52
|
+
public handlePacketStream(stream: Duplex<Packet>): ServerRPC {
|
|
53
53
|
const rpc = this.startRpc()
|
|
54
|
-
|
|
54
|
+
pipe(stream, rpc, stream)
|
|
55
|
+
return rpc
|
|
55
56
|
}
|
|
56
57
|
}
|
package/e2e/debug.test
DELETED
|
Binary file
|