starpc 0.49.17 → 0.49.20
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/README.md +1 -3
- package/cmd/protoc-gen-es-starpc/typescript.ts +11 -6
- package/dist/cmd/protoc-gen-es-starpc/typescript.js +6 -5
- package/dist/echo/echo.pb.d.ts +1 -1
- package/dist/echo/echo.pb.js +3 -3
- package/dist/integration/cross-language/ts-client.js +84 -11
- package/dist/integration/cross-language/ts-server.js +84 -4
- package/dist/mock/mock.pb.d.ts +1 -1
- package/dist/mock/mock.pb.js +3 -3
- package/dist/rpcstream/rpcstream.pb.d.ts +1 -1
- package/dist/rpcstream/rpcstream.pb.js +14 -8
- package/dist/srpc/call-receipt.d.ts +17 -0
- package/dist/srpc/call-receipt.js +105 -0
- package/dist/srpc/call-receipt.test.d.ts +1 -0
- package/dist/srpc/call-receipt.test.js +374 -0
- package/dist/srpc/client.d.ts +3 -1
- package/dist/srpc/client.js +20 -0
- package/dist/srpc/common-rpc.d.ts +13 -1
- package/dist/srpc/common-rpc.js +86 -6
- package/dist/srpc/handler.d.ts +2 -1
- package/dist/srpc/index.d.ts +4 -0
- package/dist/srpc/index.js +2 -0
- package/dist/srpc/invoker.d.ts +2 -1
- package/dist/srpc/invoker.js +2 -2
- package/dist/srpc/rpcproto.pb.d.ts +1 -1
- package/dist/srpc/rpcproto.pb.js +7 -7
- package/dist/srpc/server-invocation.d.ts +17 -0
- package/dist/srpc/server-invocation.js +37 -0
- package/dist/srpc/server-rpc.js +3 -1
- package/echo/echo.pb.go +1 -1
- package/echo/echo.pb.ts +6 -5
- package/echo/echo_srpc.pb.cpp +1 -1
- package/echo/echo_srpc.pb.go +1 -1
- package/echo/echo_srpc.pb.hpp +1 -1
- package/echo/echo_srpc.pb.rs +1 -1
- package/integration/cross-language/go-client/main.go +137 -5
- package/integration/cross-language/go-server/main.go +146 -3
- package/integration/cross-language/run.bash +117 -13
- package/integration/cross-language/ts-client.ts +94 -13
- package/integration/cross-language/ts-server.ts +94 -6
- package/mock/mock.pb.go +1 -1
- package/mock/mock.pb.ts +6 -5
- package/mock/mock_srpc.pb.cpp +1 -1
- package/mock/mock_srpc.pb.go +1 -1
- package/mock/mock_srpc.pb.hpp +1 -1
- package/mock/mock_srpc.pb.rs +1 -1
- package/package.json +1 -1
- package/srpc/call-receipt-e2e_test.go +111 -0
- package/srpc/call-receipt.go +112 -0
- package/srpc/call-receipt.test.ts +438 -0
- package/srpc/call-receipt.ts +130 -0
- package/srpc/call-receipt_test.go +536 -0
- package/srpc/client-rpc.go +1 -8
- package/srpc/client.ts +29 -2
- package/srpc/common-rpc.go +134 -29
- package/srpc/common-rpc.ts +98 -8
- package/srpc/common-rpc_test.go +52 -0
- package/srpc/handler.ts +2 -0
- package/srpc/index.ts +4 -0
- package/srpc/invoker.ts +10 -5
- package/srpc/msg-stream.go +8 -0
- package/srpc/muxed-conn.go +8 -3
- package/srpc/muxed-conn_test.go +79 -0
- package/srpc/rpcproto.pb.go +1 -1
- package/srpc/rpcproto.pb.ts +28 -27
- package/srpc/server-invocation.go +58 -0
- package/srpc/server-invocation.ts +82 -0
- package/srpc/server-rpc-invoke.go +7 -0
- package/srpc/server-rpc-invoke_goscript.go +12 -0
- package/srpc/server-rpc-invoke_goscript_test.go +27 -0
- package/srpc/server-rpc.go +9 -2
- package/srpc/server-rpc.ts +6 -2
- package/srpc/stream.go +20 -0
package/srpc/rpcproto.pb.ts
CHANGED
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
// @generated from file github.com/aperturerobotics/starpc/srpc/rpcproto.proto (package srpc, syntax proto3)
|
|
3
3
|
/* eslint-disable */
|
|
4
4
|
|
|
5
|
-
import type { MessageType
|
|
6
|
-
import { createMessageType
|
|
5
|
+
import type { MessageType } from '@aptre/protobuf-es-lite/message'
|
|
6
|
+
import { createMessageType } from '@aptre/protobuf-es-lite/message'
|
|
7
|
+
import { ScalarType } from '@aptre/protobuf-es-lite/scalar'
|
|
8
|
+
import type { PartialFieldInfo } from '@aptre/protobuf-es-lite/field'
|
|
7
9
|
|
|
8
10
|
export const protobufPackage = 'srpc'
|
|
9
11
|
|
|
@@ -42,17 +44,17 @@ export interface CallStart {
|
|
|
42
44
|
dataIsZero?: boolean
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
})
|
|
47
|
+
export const CallStart: MessageType<CallStart> =
|
|
48
|
+
/* @__PURE__ */ createMessageType({
|
|
49
|
+
typeName: 'srpc.CallStart',
|
|
50
|
+
fields: [
|
|
51
|
+
{ no: 1, name: 'rpc_service', kind: 'scalar', T: ScalarType.STRING },
|
|
52
|
+
{ no: 2, name: 'rpc_method', kind: 'scalar', T: ScalarType.STRING },
|
|
53
|
+
{ no: 3, name: 'data', kind: 'scalar', T: ScalarType.BYTES },
|
|
54
|
+
{ no: 4, name: 'data_is_zero', kind: 'scalar', T: ScalarType.BOOL },
|
|
55
|
+
] satisfies readonly PartialFieldInfo[],
|
|
56
|
+
packedByDefault: true,
|
|
57
|
+
})
|
|
56
58
|
|
|
57
59
|
/**
|
|
58
60
|
* CallData contains a message in a streaming RPC sequence.
|
|
@@ -87,17 +89,17 @@ export interface CallData {
|
|
|
87
89
|
error?: string
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
})
|
|
92
|
+
export const CallData: MessageType<CallData> =
|
|
93
|
+
/* @__PURE__ */ createMessageType({
|
|
94
|
+
typeName: 'srpc.CallData',
|
|
95
|
+
fields: [
|
|
96
|
+
{ no: 1, name: 'data', kind: 'scalar', T: ScalarType.BYTES },
|
|
97
|
+
{ no: 2, name: 'data_is_zero', kind: 'scalar', T: ScalarType.BOOL },
|
|
98
|
+
{ no: 3, name: 'complete', kind: 'scalar', T: ScalarType.BOOL },
|
|
99
|
+
{ no: 4, name: 'error', kind: 'scalar', T: ScalarType.STRING },
|
|
100
|
+
] satisfies readonly PartialFieldInfo[],
|
|
101
|
+
packedByDefault: true,
|
|
102
|
+
})
|
|
101
103
|
|
|
102
104
|
/**
|
|
103
105
|
* Packet is a message sent over a srpc packet connection.
|
|
@@ -144,8 +146,7 @@ export interface Packet {
|
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
|
|
148
|
-
export const Packet: MessageType<Packet> = createMessageType({
|
|
149
|
+
export const Packet: MessageType<Packet> = /* @__PURE__ */ createMessageType({
|
|
149
150
|
typeName: 'srpc.Packet',
|
|
150
151
|
fields: [
|
|
151
152
|
{
|
|
@@ -169,6 +170,6 @@ export const Packet: MessageType<Packet> = createMessageType({
|
|
|
169
170
|
T: ScalarType.BOOL,
|
|
170
171
|
oneof: 'body',
|
|
171
172
|
},
|
|
172
|
-
]
|
|
173
|
+
] satisfies readonly PartialFieldInfo[],
|
|
173
174
|
packedByDefault: true,
|
|
174
175
|
})
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
package srpc
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
// TerminalKind is the terminal state of a held unary invocation.
|
|
8
|
+
type TerminalKind int
|
|
9
|
+
|
|
10
|
+
const (
|
|
11
|
+
// TerminalCommitted identifies an explicit remote CallData completion.
|
|
12
|
+
TerminalCommitted TerminalKind = iota
|
|
13
|
+
// TerminalCanceled identifies a remote CallCancel packet.
|
|
14
|
+
TerminalCanceled
|
|
15
|
+
// TerminalLost identifies a remote error or transport failure.
|
|
16
|
+
TerminalLost
|
|
17
|
+
// TerminalClosed identifies a bare remote close without completion.
|
|
18
|
+
TerminalClosed
|
|
19
|
+
// TerminalAbandoned identifies owner-context expiry without a remote terminal.
|
|
20
|
+
TerminalAbandoned
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
// ServerInvocation exposes the terminal of a held unary server invocation.
|
|
24
|
+
type ServerInvocation interface {
|
|
25
|
+
// WaitTerminal waits for a remote terminal or owner-context expiry. The
|
|
26
|
+
// owner context must outlive the invocation context.
|
|
27
|
+
WaitTerminal(ownerCtx context.Context) (TerminalKind, error)
|
|
28
|
+
// Done returns the invocation context cancellation channel for diagnostics.
|
|
29
|
+
Done() <-chan struct{}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
type serverInvocationKey struct{}
|
|
33
|
+
|
|
34
|
+
func withServerInvocation(ctx context.Context, invocation ServerInvocation) context.Context {
|
|
35
|
+
return context.WithValue(ctx, serverInvocationKey{}, invocation)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// GetServerInvocation returns the held unary invocation attached to a context.
|
|
39
|
+
func GetServerInvocation(ctx context.Context) (ServerInvocation, bool) {
|
|
40
|
+
if ctx == nil {
|
|
41
|
+
return nil, false
|
|
42
|
+
}
|
|
43
|
+
invocation, ok := ctx.Value(serverInvocationKey{}).(ServerInvocation)
|
|
44
|
+
return invocation, ok
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// WaitTerminal waits for and classifies the terminal of a held unary invocation.
|
|
48
|
+
func (r *ServerRPC) WaitTerminal(ownerCtx context.Context) (TerminalKind, error) {
|
|
49
|
+
return r.commonRPC.WaitTerminal(ownerCtx)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Done returns the invocation context cancellation channel for diagnostics.
|
|
53
|
+
func (r *ServerRPC) Done() <-chan struct{} {
|
|
54
|
+
return r.ctx.Done()
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// _ is a type assertion.
|
|
58
|
+
var _ ServerInvocation = (*ServerRPC)(nil)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// TerminalKind is the terminal state of a held unary invocation.
|
|
2
|
+
export type TerminalKind =
|
|
3
|
+
| 'committed'
|
|
4
|
+
| 'canceled'
|
|
5
|
+
| 'transportLost'
|
|
6
|
+
| 'closed'
|
|
7
|
+
| 'abandoned'
|
|
8
|
+
|
|
9
|
+
// ServerInvocation exposes the invocation signal and terminal wait.
|
|
10
|
+
export class ServerInvocation implements AbortSignal {
|
|
11
|
+
public constructor(
|
|
12
|
+
public readonly signal: AbortSignal,
|
|
13
|
+
private readonly waitFn: (
|
|
14
|
+
ownerSignal: AbortSignal,
|
|
15
|
+
) => Promise<TerminalKind>,
|
|
16
|
+
) {}
|
|
17
|
+
|
|
18
|
+
public get aborted(): boolean {
|
|
19
|
+
return this.signal.aborted
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public get onabort() {
|
|
23
|
+
return this.signal.onabort
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
public set onabort(value: typeof this.signal.onabort) {
|
|
27
|
+
this.signal.onabort = value
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public get reason() {
|
|
31
|
+
return this.signal.reason
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
public throwIfAborted(): void {
|
|
35
|
+
this.signal.throwIfAborted()
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public addEventListener<K extends keyof AbortSignalEventMap>(
|
|
39
|
+
type: K,
|
|
40
|
+
listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => unknown,
|
|
41
|
+
options?: boolean | AddEventListenerOptions,
|
|
42
|
+
): void
|
|
43
|
+
public addEventListener(
|
|
44
|
+
type: string,
|
|
45
|
+
listener: EventListenerOrEventListenerObject,
|
|
46
|
+
options?: boolean | AddEventListenerOptions,
|
|
47
|
+
): void
|
|
48
|
+
public addEventListener(
|
|
49
|
+
type: string,
|
|
50
|
+
listener: EventListenerOrEventListenerObject,
|
|
51
|
+
options?: boolean | AddEventListenerOptions,
|
|
52
|
+
): void {
|
|
53
|
+
this.signal.addEventListener(type, listener, options)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public removeEventListener<K extends keyof AbortSignalEventMap>(
|
|
57
|
+
type: K,
|
|
58
|
+
listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => unknown,
|
|
59
|
+
options?: boolean | EventListenerOptions,
|
|
60
|
+
): void
|
|
61
|
+
public removeEventListener(
|
|
62
|
+
type: string,
|
|
63
|
+
listener: EventListenerOrEventListenerObject,
|
|
64
|
+
options?: boolean | EventListenerOptions,
|
|
65
|
+
): void
|
|
66
|
+
public removeEventListener(
|
|
67
|
+
type: string,
|
|
68
|
+
listener: EventListenerOrEventListenerObject,
|
|
69
|
+
options?: boolean | EventListenerOptions,
|
|
70
|
+
): void {
|
|
71
|
+
this.signal.removeEventListener(type, listener, options)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public dispatchEvent(event: Event): boolean {
|
|
75
|
+
return this.signal.dispatchEvent(event)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// waitTerminal waits for a remote terminal or owner-signal cancellation.
|
|
79
|
+
public waitTerminal(ownerSignal: AbortSignal): Promise<TerminalKind> {
|
|
80
|
+
return this.waitFn(ownerSignal)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//go:build goscript
|
|
2
|
+
|
|
3
|
+
package srpc
|
|
4
|
+
|
|
5
|
+
import (
|
|
6
|
+
"testing"
|
|
7
|
+
"time"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
func TestStartServerRPCInvokeDefersGoscriptWork(t *testing.T) {
|
|
11
|
+
ran := make(chan struct{}, 1)
|
|
12
|
+
startServerRPCInvoke(func() {
|
|
13
|
+
ran <- struct{}{}
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
select {
|
|
17
|
+
case <-ran:
|
|
18
|
+
t.Fatal("server rpc invoke ran inline")
|
|
19
|
+
default:
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
select {
|
|
23
|
+
case <-ran:
|
|
24
|
+
case <-time.After(time.Second):
|
|
25
|
+
t.Fatal("server rpc invoke did not run")
|
|
26
|
+
}
|
|
27
|
+
}
|
package/srpc/server-rpc.go
CHANGED
|
@@ -80,9 +80,16 @@ func (r *ServerRPC) HandleCallStart(pkt *CallStart) error {
|
|
|
80
80
|
r.dataQueue = append(r.dataQueue, data)
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
// Wait is used as a resource lifetime barrier by rpcstream components.
|
|
84
|
+
// Mark the method active before scheduling it so cancellation cannot make
|
|
85
|
+
// Wait return and release a mux while invokeRPC is still running user code.
|
|
86
|
+
r.localActive = true
|
|
87
|
+
|
|
83
88
|
// invoke the rpc
|
|
84
89
|
locked.Broadcast()
|
|
85
|
-
|
|
90
|
+
startServerRPCInvoke(func() {
|
|
91
|
+
r.invokeRPC(service, method)
|
|
92
|
+
})
|
|
86
93
|
locked.Unlock()
|
|
87
94
|
|
|
88
95
|
return err
|
|
@@ -91,7 +98,7 @@ func (r *ServerRPC) HandleCallStart(pkt *CallStart) error {
|
|
|
91
98
|
// invokeRPC invokes the RPC after CallStart is received.
|
|
92
99
|
func (r *ServerRPC) invokeRPC(serviceID, methodID string) {
|
|
93
100
|
// on the server side, the writer is closed by invokeRPC.
|
|
94
|
-
strm := NewMsgStream(r.ctx, r, r.cancelContext)
|
|
101
|
+
strm := NewMsgStream(withServerInvocation(r.ctx, r), r, r.cancelContext)
|
|
95
102
|
ok, err := r.invoker.InvokeMethod(serviceID, methodID, strm)
|
|
96
103
|
if err == nil && !ok {
|
|
97
104
|
err = ErrUnimplemented
|
package/srpc/server-rpc.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { Sink, Source } from 'it-stream-types'
|
|
|
3
3
|
import type { CallData, CallStart } from './rpcproto.pb.js'
|
|
4
4
|
import { CommonRPC } from './common-rpc.js'
|
|
5
5
|
import { InvokeFn } from './handler.js'
|
|
6
|
+
import { ServerInvocation } from './server-invocation.js'
|
|
6
7
|
import { LookupMethod } from './mux.js'
|
|
7
8
|
|
|
8
9
|
// ServerRPC is an ongoing RPC from the server side.
|
|
@@ -43,12 +44,15 @@ export class ServerRPC extends CommonRPC {
|
|
|
43
44
|
}
|
|
44
45
|
return super.handleCallData(packet)
|
|
45
46
|
}
|
|
46
|
-
|
|
47
47
|
// invokeRPC starts invoking the RPC handler.
|
|
48
48
|
private async invokeRPC(invokeFn: InvokeFn) {
|
|
49
49
|
const dataSink = this._createDataSink()
|
|
50
|
+
const invocation = new ServerInvocation(
|
|
51
|
+
this.invocationSignal,
|
|
52
|
+
(ownerSignal) => this.waitTerminal(ownerSignal),
|
|
53
|
+
)
|
|
50
54
|
try {
|
|
51
|
-
await invokeFn(this.rpcDataSource, dataSink)
|
|
55
|
+
await invokeFn(this.rpcDataSource, dataSink, invocation)
|
|
52
56
|
} catch (err) {
|
|
53
57
|
this.close(err as Error)
|
|
54
58
|
}
|
package/srpc/stream.go
CHANGED
|
@@ -23,6 +23,10 @@ type Stream interface {
|
|
|
23
23
|
Close() error
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
type receiptTerminalStream interface {
|
|
27
|
+
receiptTerminalKind() (TerminalKind, bool)
|
|
28
|
+
}
|
|
29
|
+
|
|
26
30
|
// StreamRecv is a stream that can receive typed messages.
|
|
27
31
|
//
|
|
28
32
|
// T is the response type.
|
|
@@ -69,6 +73,14 @@ func (s *streamWithClose) Close() error {
|
|
|
69
73
|
return err2
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
func (s *streamWithClose) receiptTerminalKind() (TerminalKind, bool) {
|
|
77
|
+
receipt, ok := s.Stream.(receiptTerminalStream)
|
|
78
|
+
if !ok {
|
|
79
|
+
return 0, false
|
|
80
|
+
}
|
|
81
|
+
return receipt.receiptTerminalKind()
|
|
82
|
+
}
|
|
83
|
+
|
|
72
84
|
// streamWithContext is a Stream with a wrapped Context function.
|
|
73
85
|
type streamWithContext struct {
|
|
74
86
|
Stream
|
|
@@ -85,6 +97,14 @@ func (s *streamWithContext) Context() context.Context {
|
|
|
85
97
|
return s.ctx
|
|
86
98
|
}
|
|
87
99
|
|
|
100
|
+
func (s *streamWithContext) receiptTerminalKind() (TerminalKind, bool) {
|
|
101
|
+
receipt, ok := s.Stream.(receiptTerminalStream)
|
|
102
|
+
if !ok {
|
|
103
|
+
return 0, false
|
|
104
|
+
}
|
|
105
|
+
return receipt.receiptTerminalKind()
|
|
106
|
+
}
|
|
107
|
+
|
|
88
108
|
// _ is a type assertion
|
|
89
109
|
var (
|
|
90
110
|
_ Stream = (*streamWithClose)(nil)
|