starpc 0.49.18 → 0.50.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.
Files changed (86) hide show
  1. package/README.md +1 -3
  2. package/cmd/protoc-gen-es-starpc/typescript.ts +11 -6
  3. package/dist/cmd/protoc-gen-es-starpc/typescript.js +6 -5
  4. package/dist/echo/echo.pb.d.ts +1 -1
  5. package/dist/echo/echo.pb.js +3 -3
  6. package/dist/integration/cross-language/ts-client.js +84 -11
  7. package/dist/integration/cross-language/ts-server.js +100 -4
  8. package/dist/mock/index.d.ts +2 -2
  9. package/dist/mock/index.js +2 -2
  10. package/dist/mock/mock.pb.d.ts +1 -1
  11. package/dist/mock/mock.pb.js +3 -3
  12. package/dist/rpcstream/receipt.test.d.ts +1 -0
  13. package/dist/rpcstream/receipt.test.js +41 -0
  14. package/dist/rpcstream/rpcstream.pb.d.ts +1 -1
  15. package/dist/rpcstream/rpcstream.pb.js +14 -8
  16. package/dist/srpc/call-receipt.d.ts +17 -0
  17. package/dist/srpc/call-receipt.js +106 -0
  18. package/dist/srpc/call-receipt.test.d.ts +1 -0
  19. package/dist/srpc/call-receipt.test.js +375 -0
  20. package/dist/srpc/client.d.ts +3 -1
  21. package/dist/srpc/client.js +20 -0
  22. package/dist/srpc/common-rpc.d.ts +13 -3
  23. package/dist/srpc/common-rpc.js +87 -10
  24. package/dist/srpc/handler.d.ts +2 -1
  25. package/dist/srpc/index.d.ts +4 -0
  26. package/dist/srpc/index.js +3 -0
  27. package/dist/srpc/invoker.d.ts +2 -1
  28. package/dist/srpc/invoker.js +2 -2
  29. package/dist/srpc/rpcproto.pb.d.ts +45 -1
  30. package/dist/srpc/rpcproto.pb.js +60 -7
  31. package/dist/srpc/server-invocation.d.ts +17 -0
  32. package/dist/srpc/server-invocation.js +37 -0
  33. package/dist/srpc/server-rpc.js +3 -1
  34. package/echo/echo.pb.go +1 -1
  35. package/echo/echo.pb.ts +6 -5
  36. package/echo/echo_srpc.pb.cpp +1 -1
  37. package/echo/echo_srpc.pb.go +1 -1
  38. package/echo/echo_srpc.pb.hpp +1 -1
  39. package/echo/echo_srpc.pb.rs +1 -1
  40. package/go.mod +13 -12
  41. package/go.sum +24 -24
  42. package/integration/cross-language/go-client/main.go +137 -5
  43. package/integration/cross-language/go-server/fixture-owner_test.go +127 -0
  44. package/integration/cross-language/go-server/main.go +169 -4
  45. package/integration/cross-language/run.bash +117 -13
  46. package/integration/cross-language/ts-client.ts +94 -13
  47. package/integration/cross-language/ts-server.ts +115 -7
  48. package/mock/index.ts +2 -2
  49. package/mock/mock.pb.go +1 -1
  50. package/mock/mock.pb.ts +6 -5
  51. package/mock/mock_srpc.pb.cpp +1 -1
  52. package/mock/mock_srpc.pb.go +1 -1
  53. package/mock/mock_srpc.pb.hpp +1 -1
  54. package/mock/mock_srpc.pb.rs +1 -1
  55. package/package.json +19 -26
  56. package/srpc/accept.go +1 -1
  57. package/srpc/call-receipt-e2e_test.go +111 -0
  58. package/srpc/call-receipt.go +112 -0
  59. package/srpc/call-receipt.test.ts +441 -0
  60. package/srpc/call-receipt.ts +131 -0
  61. package/srpc/call-receipt_test.go +536 -0
  62. package/srpc/client-rpc.go +1 -8
  63. package/srpc/client.ts +29 -2
  64. package/srpc/common-rpc.go +114 -29
  65. package/srpc/common-rpc.ts +104 -13
  66. package/srpc/handler.ts +2 -0
  67. package/srpc/index.ts +4 -0
  68. package/srpc/invoker.ts +10 -5
  69. package/srpc/msg-stream.go +8 -0
  70. package/srpc/muxed-conn.go +9 -4
  71. package/srpc/muxed-conn_test.go +79 -0
  72. package/srpc/muxed-yamux.go +1 -1
  73. package/srpc/rpcproto.pb.cc +15 -4
  74. package/srpc/rpcproto.pb.go +97 -1
  75. package/srpc/rpcproto.pb.h +59 -0
  76. package/srpc/rpcproto.pb.rs +45 -0
  77. package/srpc/rpcproto.pb.ts +90 -27
  78. package/srpc/rpcproto.proto +16 -0
  79. package/srpc/schema-ownership_test.go +115 -0
  80. package/srpc/server-invocation.go +40 -0
  81. package/srpc/server-invocation.ts +76 -0
  82. package/srpc/server-rpc.go +1 -1
  83. package/srpc/server-rpc.ts +6 -2
  84. package/srpc/stream-yamux.go +1 -1
  85. package/srpc/stream.go +20 -0
  86. package/srpc/websocket.go +1 -1
@@ -6,7 +6,7 @@ import { MethodKind } from '@aptre/protobuf-es-lite';
6
6
  // createInvokeFn builds an InvokeFn from a method definition and a function prototype.
7
7
  export function createInvokeFn(methodInfo, methodProto) {
8
8
  const requestDecode = buildDecodeMessageTransform(methodInfo.I);
9
- return async (dataSource, dataSink) => {
9
+ return async (dataSource, dataSink, invocation) => {
10
10
  // responseSink is a Sink for response messages.
11
11
  const responseSink = pushable({
12
12
  objectMode: true,
@@ -34,7 +34,7 @@ export function createInvokeFn(methodInfo, methodProto) {
34
34
  }
35
35
  // Call the implementation.
36
36
  try {
37
- const responseObj = methodProto(requestArg);
37
+ const responseObj = methodProto(requestArg, invocation);
38
38
  if (!responseObj) {
39
39
  throw new Error('return value was undefined');
40
40
  }
@@ -1,5 +1,49 @@
1
- import type { MessageType } from '@aptre/protobuf-es-lite';
1
+ import type { MessageType } from '@aptre/protobuf-es-lite/message';
2
2
  export declare const protobufPackage = "srpc";
3
+ /**
4
+ * TerminalKind is the terminal state of a held unary invocation.
5
+ *
6
+ * @generated from enum srpc.TerminalKind
7
+ */
8
+ export declare enum TerminalKind {
9
+ /**
10
+ * TERMINAL_KIND_UNSPECIFIED is not a valid terminal state.
11
+ *
12
+ * @generated from enum value: TERMINAL_KIND_UNSPECIFIED = 0;
13
+ */
14
+ UNSPECIFIED = 0,
15
+ /**
16
+ * TERMINAL_KIND_COMMITTED identifies an explicit remote CallData completion.
17
+ *
18
+ * @generated from enum value: TERMINAL_KIND_COMMITTED = 1;
19
+ */
20
+ COMMITTED = 1,
21
+ /**
22
+ * TERMINAL_KIND_CANCELED identifies a remote CallCancel packet.
23
+ *
24
+ * @generated from enum value: TERMINAL_KIND_CANCELED = 2;
25
+ */
26
+ CANCELED = 2,
27
+ /**
28
+ * TERMINAL_KIND_TRANSPORT_LOST identifies a remote error or transport failure.
29
+ *
30
+ * @generated from enum value: TERMINAL_KIND_TRANSPORT_LOST = 3;
31
+ */
32
+ TRANSPORT_LOST = 3,
33
+ /**
34
+ * TERMINAL_KIND_CLOSED identifies a bare remote close without completion.
35
+ *
36
+ * @generated from enum value: TERMINAL_KIND_CLOSED = 4;
37
+ */
38
+ CLOSED = 4,
39
+ /**
40
+ * TERMINAL_KIND_ABANDONED identifies owner-context expiry without a remote terminal.
41
+ *
42
+ * @generated from enum value: TERMINAL_KIND_ABANDONED = 5;
43
+ */
44
+ ABANDONED = 5
45
+ }
46
+ export declare const TerminalKind_Enum: import("@aptre/protobuf-es-lite/enum").EnumType;
3
47
  /**
4
48
  * CallStart requests starting a new RPC call.
5
49
  *
@@ -1,10 +1,64 @@
1
1
  // @generated by protoc-gen-es-lite unknown with parameter "target=ts,ts_nocheck=false"
2
2
  // @generated from file github.com/aperturerobotics/starpc/srpc/rpcproto.proto (package srpc, syntax proto3)
3
3
  /* eslint-disable */
4
- import { createMessageType, ScalarType } from '@aptre/protobuf-es-lite';
4
+ import { createEnumType } from '@aptre/protobuf-es-lite/enum';
5
+ import { createMessageType } from '@aptre/protobuf-es-lite/message';
6
+ import { ScalarType } from '@aptre/protobuf-es-lite/scalar';
5
7
  export const protobufPackage = 'srpc';
6
- // CallStart contains the message type declaration for CallStart.
7
- export const CallStart = createMessageType({
8
+ /**
9
+ * TerminalKind is the terminal state of a held unary invocation.
10
+ *
11
+ * @generated from enum srpc.TerminalKind
12
+ */
13
+ export var TerminalKind;
14
+ (function (TerminalKind) {
15
+ /**
16
+ * TERMINAL_KIND_UNSPECIFIED is not a valid terminal state.
17
+ *
18
+ * @generated from enum value: TERMINAL_KIND_UNSPECIFIED = 0;
19
+ */
20
+ TerminalKind[TerminalKind["UNSPECIFIED"] = 0] = "UNSPECIFIED";
21
+ /**
22
+ * TERMINAL_KIND_COMMITTED identifies an explicit remote CallData completion.
23
+ *
24
+ * @generated from enum value: TERMINAL_KIND_COMMITTED = 1;
25
+ */
26
+ TerminalKind[TerminalKind["COMMITTED"] = 1] = "COMMITTED";
27
+ /**
28
+ * TERMINAL_KIND_CANCELED identifies a remote CallCancel packet.
29
+ *
30
+ * @generated from enum value: TERMINAL_KIND_CANCELED = 2;
31
+ */
32
+ TerminalKind[TerminalKind["CANCELED"] = 2] = "CANCELED";
33
+ /**
34
+ * TERMINAL_KIND_TRANSPORT_LOST identifies a remote error or transport failure.
35
+ *
36
+ * @generated from enum value: TERMINAL_KIND_TRANSPORT_LOST = 3;
37
+ */
38
+ TerminalKind[TerminalKind["TRANSPORT_LOST"] = 3] = "TRANSPORT_LOST";
39
+ /**
40
+ * TERMINAL_KIND_CLOSED identifies a bare remote close without completion.
41
+ *
42
+ * @generated from enum value: TERMINAL_KIND_CLOSED = 4;
43
+ */
44
+ TerminalKind[TerminalKind["CLOSED"] = 4] = "CLOSED";
45
+ /**
46
+ * TERMINAL_KIND_ABANDONED identifies owner-context expiry without a remote terminal.
47
+ *
48
+ * @generated from enum value: TERMINAL_KIND_ABANDONED = 5;
49
+ */
50
+ TerminalKind[TerminalKind["ABANDONED"] = 5] = "ABANDONED";
51
+ })(TerminalKind || (TerminalKind = {}));
52
+ export const TerminalKind_Enum = /* @__PURE__ */ createEnumType('srpc.TerminalKind', [
53
+ [0, 'TERMINAL_KIND_UNSPECIFIED'],
54
+ [1, 'TERMINAL_KIND_COMMITTED'],
55
+ [2, 'TERMINAL_KIND_CANCELED'],
56
+ [3, 'TERMINAL_KIND_TRANSPORT_LOST'],
57
+ [4, 'TERMINAL_KIND_CLOSED'],
58
+ [5, 'TERMINAL_KIND_ABANDONED'],
59
+ ]);
60
+ export const CallStart =
61
+ /* @__PURE__ */ createMessageType({
8
62
  typeName: 'srpc.CallStart',
9
63
  fields: [
10
64
  { no: 1, name: 'rpc_service', kind: 'scalar', T: ScalarType.STRING },
@@ -14,8 +68,8 @@ export const CallStart = createMessageType({
14
68
  ],
15
69
  packedByDefault: true,
16
70
  });
17
- // CallData contains the message type declaration for CallData.
18
- export const CallData = createMessageType({
71
+ export const CallData =
72
+ /* @__PURE__ */ createMessageType({
19
73
  typeName: 'srpc.CallData',
20
74
  fields: [
21
75
  { no: 1, name: 'data', kind: 'scalar', T: ScalarType.BYTES },
@@ -25,8 +79,7 @@ export const CallData = createMessageType({
25
79
  ],
26
80
  packedByDefault: true,
27
81
  });
28
- // Packet contains the message type declaration for Packet.
29
- export const Packet = createMessageType({
82
+ export const Packet = /* @__PURE__ */ createMessageType({
30
83
  typeName: 'srpc.Packet',
31
84
  fields: [
32
85
  {
@@ -0,0 +1,17 @@
1
+ import type { TerminalKind } from './rpcproto.pb.js';
2
+ export declare class ServerInvocation implements AbortSignal {
3
+ readonly signal: AbortSignal;
4
+ private readonly waitFn;
5
+ constructor(signal: AbortSignal, waitFn: (ownerSignal: AbortSignal) => Promise<TerminalKind>);
6
+ get aborted(): boolean;
7
+ get onabort(): typeof this.signal.onabort;
8
+ set onabort(value: typeof this.signal.onabort);
9
+ get reason(): any;
10
+ throwIfAborted(): void;
11
+ addEventListener<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => unknown, options?: boolean | AddEventListenerOptions): void;
12
+ addEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
13
+ removeEventListener<K extends keyof AbortSignalEventMap>(type: K, listener: (this: AbortSignal, ev: AbortSignalEventMap[K]) => unknown, options?: boolean | EventListenerOptions): void;
14
+ removeEventListener(type: string, listener: EventListenerOrEventListenerObject, options?: boolean | EventListenerOptions): void;
15
+ dispatchEvent(event: Event): boolean;
16
+ waitTerminal(ownerSignal: AbortSignal): Promise<TerminalKind>;
17
+ }
@@ -0,0 +1,37 @@
1
+ // ServerInvocation exposes the invocation signal and terminal wait.
2
+ export class ServerInvocation {
3
+ signal;
4
+ waitFn;
5
+ constructor(signal, waitFn) {
6
+ this.signal = signal;
7
+ this.waitFn = waitFn;
8
+ }
9
+ get aborted() {
10
+ return this.signal.aborted;
11
+ }
12
+ get onabort() {
13
+ return this.signal.onabort;
14
+ }
15
+ set onabort(value) {
16
+ this.signal.onabort = value;
17
+ }
18
+ get reason() {
19
+ return this.signal.reason;
20
+ }
21
+ throwIfAborted() {
22
+ this.signal.throwIfAborted();
23
+ }
24
+ addEventListener(type, listener, options) {
25
+ this.signal.addEventListener(type, listener, options);
26
+ }
27
+ removeEventListener(type, listener, options) {
28
+ this.signal.removeEventListener(type, listener, options);
29
+ }
30
+ dispatchEvent(event) {
31
+ return this.signal.dispatchEvent(event);
32
+ }
33
+ // waitTerminal waits for a remote terminal or owner-signal cancellation.
34
+ waitTerminal(ownerSignal) {
35
+ return this.waitFn(ownerSignal);
36
+ }
37
+ }
@@ -1,4 +1,5 @@
1
1
  import { CommonRPC } from './common-rpc.js';
2
+ import { ServerInvocation } from './server-invocation.js';
2
3
  // ServerRPC is an ongoing RPC from the server side.
3
4
  export class ServerRPC extends CommonRPC {
4
5
  // lookupMethod looks up the incoming RPC methods.
@@ -37,8 +38,9 @@ export class ServerRPC extends CommonRPC {
37
38
  // invokeRPC starts invoking the RPC handler.
38
39
  async invokeRPC(invokeFn) {
39
40
  const dataSink = this._createDataSink();
41
+ const invocation = new ServerInvocation(this.invocationSignal, (ownerSignal) => this.waitTerminal(ownerSignal));
40
42
  try {
41
- await invokeFn(this.rpcDataSource, dataSink);
43
+ await invokeFn(this.rpcDataSource, dataSink, invocation);
42
44
  }
43
45
  catch (err) {
44
46
  this.close(err);
package/echo/echo.pb.go CHANGED
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-go-lite. DO NOT EDIT.
2
- // protoc-gen-go-lite version: v0.12.1
2
+ // protoc-gen-go-lite version: v0.13.0
3
3
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
4
4
 
5
5
  package echo
package/echo/echo.pb.ts CHANGED
@@ -2,8 +2,10 @@
2
2
  // @generated from file github.com/aperturerobotics/starpc/echo/echo.proto (package echo, syntax proto3)
3
3
  /* eslint-disable */
4
4
 
5
- import type { MessageType, PartialFieldInfo } from '@aptre/protobuf-es-lite'
6
- import { createMessageType, ScalarType } from '@aptre/protobuf-es-lite'
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 = 'echo'
9
11
 
@@ -19,11 +21,10 @@ export interface EchoMsg {
19
21
  body?: string
20
22
  }
21
23
 
22
- // EchoMsg contains the message type declaration for EchoMsg.
23
- export const EchoMsg: MessageType<EchoMsg> = createMessageType({
24
+ export const EchoMsg: MessageType<EchoMsg> = /* @__PURE__ */ createMessageType({
24
25
  typeName: 'echo.EchoMsg',
25
26
  fields: [
26
27
  { no: 1, name: 'body', kind: 'scalar', T: ScalarType.STRING },
27
- ] as readonly PartialFieldInfo[],
28
+ ] satisfies readonly PartialFieldInfo[],
28
29
  packedByDefault: true,
29
30
  })
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.46.1
4
+ // protoc-gen-starpc-cpp version: v0.49.7
5
5
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
6
6
 
7
7
  #include "echo_srpc.pb.hpp"
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-srpc. DO NOT EDIT.
2
- // protoc-gen-srpc version: v0.46.1
2
+ // protoc-gen-srpc version: v0.49.7
3
3
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
4
4
 
5
5
  package echo
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only && cgo
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.46.1
4
+ // protoc-gen-starpc-cpp version: v0.49.7
5
5
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
6
6
 
7
7
  #pragma once
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-starpc-rust. DO NOT EDIT.
2
- // protoc-gen-starpc-rust version: v0.46.1
2
+ // protoc-gen-starpc-rust version: v0.49.7
3
3
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
4
4
 
5
5
  #[allow(unused_imports)]
package/go.mod CHANGED
@@ -3,31 +3,32 @@ module github.com/aperturerobotics/starpc
3
3
  go 1.25.0
4
4
 
5
5
  require (
6
- github.com/aperturerobotics/common v0.33.1-0.20260516193515-675cfc5a0c12 // latest
7
- github.com/aperturerobotics/protobuf-go-lite v0.13.0 // latest
8
- github.com/aperturerobotics/util v1.34.5 // latest
6
+ github.com/aperturerobotics/common v0.34.4 // latest
7
+ github.com/aperturerobotics/protobuf-go-lite v0.16.0 // latest
8
+ github.com/aperturerobotics/util v1.34.9 // latest
9
9
  )
10
10
 
11
11
  require (
12
12
  github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017 // aperture-2
13
13
  github.com/aperturerobotics/cli v1.1.0 // indirect
14
- github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260329113538-218ccd8f20e0 // indirect
15
- github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260329113540-600516012db3 // indirect
16
- github.com/aperturerobotics/go-websocket v1.8.15-0.20260329113544-74dbfb8f11c6 // master
17
- github.com/aperturerobotics/json-iterator-lite v1.0.1-0.20251104042408-0c9eb8a3f726 // indirect
14
+ github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260705010911-9f53feac967b // indirect
15
+ github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260712054757-d8078c296c17 // indirect
16
+ github.com/aperturerobotics/go-websocket v1.8.15-0.20260619192713-a096778f08c1 // master
17
+ github.com/aperturerobotics/json-iterator-lite v1.1.0 // indirect
18
18
  github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4 // wasi
19
19
  )
20
20
 
21
21
  require (
22
- github.com/libp2p/go-yamux/v4 v4.0.2 // latest
23
22
  github.com/pkg/errors v0.9.1 // latest
24
- github.com/sirupsen/logrus v1.9.5-0.20260508084601-d4a50659cfd6 // latest
23
+ github.com/sirupsen/logrus v1.9.5-0.20260629095817-a23d315dfebb // latest
25
24
  )
26
25
 
26
+ require github.com/libp2p/go-yamux/v5 v5.1.0
27
+
27
28
  require (
28
29
  github.com/libp2p/go-buffer-pool v0.1.0 // indirect
29
- github.com/tetratelabs/wazero v1.11.0 // indirect
30
+ github.com/tetratelabs/wazero v1.12.0 // indirect
30
31
  github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect
31
- golang.org/x/mod v0.35.0 // indirect
32
- golang.org/x/sys v0.44.0 // indirect
32
+ golang.org/x/mod v0.38.0 // indirect
33
+ golang.org/x/sys v0.47.0 // indirect
33
34
  )
package/go.sum CHANGED
@@ -2,46 +2,46 @@ github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017 h1:3U7
2
2
  github.com/aperturerobotics/abseil-cpp v0.0.0-20260131110040-4bb56e2f9017/go.mod h1:lNSJTKECIUFAnfeSqy01kXYTYe1BHubW7198jNX3nEw=
3
3
  github.com/aperturerobotics/cli v1.1.0 h1:7a+YRC+EY3npAnTzhHV5gLCiw91KS0Ts3XwLILGOsT8=
4
4
  github.com/aperturerobotics/cli v1.1.0/go.mod h1:M7BFP9wow5ytTzMyJQOOO991fGfsUqdTI7gGEsHfTQ8=
5
- github.com/aperturerobotics/common v0.33.1-0.20260516193515-675cfc5a0c12 h1:KQiFEDyu5//G8JZ3yTN92kF2P3F+LHjSKsQikgROncU=
6
- github.com/aperturerobotics/common v0.33.1-0.20260516193515-675cfc5a0c12/go.mod h1:pW7BhBpKzVrTxN8XIz6jb3MJERZ15GDxaGQ7jcIl0Xg=
7
- github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260329113538-218ccd8f20e0 h1:6/3RSSlPEQ6LeidslB1ZCJkxW+MnfYDkvdWMDklDXw4=
8
- github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260329113538-218ccd8f20e0/go.mod h1:OBb/beWmr/pDIZAUfi86j/4tBh2v5ctTxKMqSnh9c/4=
9
- github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260329113540-600516012db3 h1:lp+V8RYcBwTX1p81swkpZn5fhw1wn2xLorzETIxRyZQ=
10
- github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260329113540-600516012db3/go.mod h1:vEq8i7EKb32+KXGtIEZjjhNns+BdsL2dUMw4uhy3578=
11
- github.com/aperturerobotics/go-websocket v1.8.15-0.20260329113544-74dbfb8f11c6 h1:Utc1F7jdCc6/HrwwIikJFXt/hXxkWIWETLp/CsG6Gl0=
12
- github.com/aperturerobotics/go-websocket v1.8.15-0.20260329113544-74dbfb8f11c6/go.mod h1:9KnSGuqxSXbdB/Oi0I6vvfPLkclfJwMGAQaDDGVgGow=
13
- github.com/aperturerobotics/json-iterator-lite v1.0.1-0.20251104042408-0c9eb8a3f726 h1:4B1F0DzuqPzb6WqgCjWaqDD7JU9RDsevQG5OP0DFBgs=
14
- github.com/aperturerobotics/json-iterator-lite v1.0.1-0.20251104042408-0c9eb8a3f726/go.mod h1:SvGGBv3OVxUyqO0ZxA/nvs6z3cg7NIbZ64TnbV2OISo=
5
+ github.com/aperturerobotics/common v0.34.4 h1:RQTOm6LxEnucXkED/kruhKKAOL6tbaPzMgfHPKaES4k=
6
+ github.com/aperturerobotics/common v0.34.4/go.mod h1:xnb1VBRs3x2o1PaKg0OlP7SqDaC9Hmrmt9xH85rOS+A=
7
+ github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260705010911-9f53feac967b h1:MMk+AbWPCMfqc2Lm2bJl8lCCsuvlM2+C0CP10fpE3bc=
8
+ github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260705010911-9f53feac967b/go.mod h1:OBb/beWmr/pDIZAUfi86j/4tBh2v5ctTxKMqSnh9c/4=
9
+ github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260712054757-d8078c296c17 h1:WT1YmZjJf8fUF1zKbeNNKXFA+t8Wtbr8AVMAmdKcrUU=
10
+ github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260712054757-d8078c296c17/go.mod h1:vEq8i7EKb32+KXGtIEZjjhNns+BdsL2dUMw4uhy3578=
11
+ github.com/aperturerobotics/go-websocket v1.8.15-0.20260619192713-a096778f08c1 h1:q2qkdNOrnhAFf6B7TBeUoAYstYWxPvozN+3vJi2jgp0=
12
+ github.com/aperturerobotics/go-websocket v1.8.15-0.20260619192713-a096778f08c1/go.mod h1:9KnSGuqxSXbdB/Oi0I6vvfPLkclfJwMGAQaDDGVgGow=
13
+ github.com/aperturerobotics/json-iterator-lite v1.1.0 h1:ZLLqHhHTKYlmCAP873ras2e/yVU/THtwC+ji3tXLMMg=
14
+ github.com/aperturerobotics/json-iterator-lite v1.1.0/go.mod h1:SvGGBv3OVxUyqO0ZxA/nvs6z3cg7NIbZ64TnbV2OISo=
15
15
  github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4 h1:4Dy3BAHh2kgVdHAqtlwcFsgY0kAwUe2m3rfFcaGwGQg=
16
16
  github.com/aperturerobotics/protobuf v0.0.0-20260203024654-8201686529c4/go.mod h1:tMgO7y6SJo/d9ZcvrpNqIQtdYT9de+QmYaHOZ4KnhOg=
17
- github.com/aperturerobotics/protobuf-go-lite v0.13.0 h1:jEvCJhHaJEikDY/va2AUnS0DOb/0n82aISLAqxSh4Sk=
18
- github.com/aperturerobotics/protobuf-go-lite v0.13.0/go.mod h1:lGH3s5ArCTXKI4wJdlNpaybUtwSjfAG0vdWjxOfMcF8=
19
- github.com/aperturerobotics/util v1.34.5 h1:007MaOJrrsiGm5o1c8Tt7p8nVwUAxkM6pmGflrBww/U=
20
- github.com/aperturerobotics/util v1.34.5/go.mod h1:mDe7WnncVuV7yjeeVSsagyfrw4xfncu7d+f0+d70niY=
17
+ github.com/aperturerobotics/protobuf-go-lite v0.16.0 h1:McGR0jrc15ZkH8HUpAARDOtazjwqr+uYXVHrrR59K28=
18
+ github.com/aperturerobotics/protobuf-go-lite v0.16.0/go.mod h1:3Ay/E7iaw2KWLirK3+dDdNJZHK0hu8Y1/kKeYeUa+8s=
19
+ github.com/aperturerobotics/util v1.34.9 h1:Eyhu1Citt+od7jKIb+Q92h/CEujWCFsvdvJavQeoRjM=
20
+ github.com/aperturerobotics/util v1.34.9/go.mod h1:GxjGkYQNEj8xvLFz+T8ifqANtK5cMesOlCxh50htvBI=
21
21
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
22
22
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
23
23
  github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
24
24
  github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
25
25
  github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
26
26
  github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
27
- github.com/libp2p/go-yamux/v4 v4.0.2 h1:nrLh89LN/LEiqcFiqdKDRHjGstN300C1269K/EX0CPU=
28
- github.com/libp2p/go-yamux/v4 v4.0.2/go.mod h1:C808cCRgOs1iBwY4S71T5oxgMxgLmqUw56qh4AeBW2o=
27
+ github.com/libp2p/go-yamux/v5 v5.1.0 h1:8Qlxj4E9JGJAQVW6+uj2o7mqkqsIVlSUGmTWhlXzoHE=
28
+ github.com/libp2p/go-yamux/v5 v5.1.0/go.mod h1:tgIQ07ObtRR/I0IWsFOyQIL9/dR5UXgc2s8xKmNZv1o=
29
29
  github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
30
30
  github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
31
31
  github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
32
32
  github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
33
- github.com/sirupsen/logrus v1.9.5-0.20260508084601-d4a50659cfd6 h1:D6qewLO/pJ+JvUwuri1dmCbc32d/eO+xyqg+p3N+9kA=
34
- github.com/sirupsen/logrus v1.9.5-0.20260508084601-d4a50659cfd6/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180=
33
+ github.com/sirupsen/logrus v1.9.5-0.20260629095817-a23d315dfebb h1:+C8W2sTMIRbihe5hQH6JuT2mi3m/QgdGf0YdFyx2azA=
34
+ github.com/sirupsen/logrus v1.9.5-0.20260629095817-a23d315dfebb/go.mod h1:FXZFonkDAnFozmO+5hGAFvB0Yg9/j2SIhA/QuIkP180=
35
35
  github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
36
36
  github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
37
- github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA=
38
- github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU=
37
+ github.com/tetratelabs/wazero v1.12.0 h1:DuWcpNu/FzgEXgGBDp8J1Spc+CWOvvtvVyjKlaZopYU=
38
+ github.com/tetratelabs/wazero v1.12.0/go.mod h1:LvKtzl2RqO4gyF27BiXU+nKAjcV8f38U+kP/q2vgxh0=
39
39
  github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 h1:FnBeRrxr7OU4VvAzt5X7s6266i6cSVkkFPS0TuXWbIg=
40
40
  github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
41
- golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
42
- golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
43
- golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
44
- golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
41
+ golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
42
+ golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
43
+ golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
44
+ golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
45
45
  google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
46
46
  google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
47
47
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
@@ -2,6 +2,7 @@ package main
2
2
 
3
3
  import (
4
4
  "context"
5
+ "errors"
5
6
  "fmt"
6
7
  "io"
7
8
  "net"
@@ -14,14 +15,32 @@ import (
14
15
  const bodyTxt = "hello world via starpc cross-language e2e test"
15
16
 
16
17
  func main() {
17
- if len(os.Args) < 2 {
18
- fmt.Fprintf(os.Stderr, "usage: go-client <addr>\n")
18
+ receiptMode := len(os.Args) > 1 && os.Args[1] == "receipt"
19
+ receiptCase := ""
20
+ addrIndex := 1
21
+ if receiptMode {
22
+ if len(os.Args) < 3 {
23
+ fmt.Fprintf(os.Stderr, "usage: go-client receipt <case> <addr>\n")
24
+ os.Exit(1)
25
+ }
26
+ receiptCase = os.Args[2]
27
+ addrIndex = 3
28
+ switch receiptCase {
29
+ case "commit", "abort", "loss", "bare-close":
30
+ default:
31
+ fmt.Fprintf(os.Stderr, "unknown receipt case: %s\n", receiptCase)
32
+ os.Exit(1)
33
+ }
34
+ }
35
+ if len(os.Args) <= addrIndex {
36
+ fmt.Fprintf(os.Stderr, "usage: go-client [receipt <case>] <addr>\n")
19
37
  os.Exit(1)
20
38
  }
21
- addr := os.Args[1]
22
-
39
+ addr := os.Args[addrIndex]
40
+ var conn net.Conn
23
41
  openStream := func(ctx context.Context, msgHandler srpc.PacketDataHandler, closeHandler srpc.CloseHandler) (srpc.PacketWriter, error) {
24
- conn, err := net.Dial("tcp", addr) //nolint:gosec
42
+ var err error
43
+ conn, err = net.Dial("tcp", addr) //nolint:gosec
25
44
  if err != nil {
26
45
  return nil, err
27
46
  }
@@ -33,6 +52,14 @@ func main() {
33
52
  client := srpc.NewClient(openStream)
34
53
  echoClient := echo.NewSRPCEchoerClient(client)
35
54
  ctx := context.Background()
55
+ if receiptMode {
56
+ if err := testReceipt(ctx, client, &conn, receiptCase); err != nil {
57
+ fmt.Fprintf(os.Stderr, "receipt test failed: %v\n", err)
58
+ os.Exit(1)
59
+ }
60
+ fmt.Println("All tests passed.")
61
+ return
62
+ }
36
63
 
37
64
  if err := testUnary(ctx, echoClient); err != nil {
38
65
  fmt.Fprintf(os.Stderr, "unary test failed: %v\n", err)
@@ -70,6 +97,79 @@ func testUnary(ctx context.Context, client echo.SRPCEchoerClient) error {
70
97
  return nil
71
98
  }
72
99
 
100
+ func testReceipt(
101
+ ctx context.Context,
102
+ client srpc.Client,
103
+ conn *net.Conn,
104
+ receiptCase string,
105
+ ) error {
106
+ fmt.Printf("Testing held unary receipt (%s)...\n", receiptCase)
107
+ out := new(echo.EchoMsg)
108
+ receipt, err := srpc.ExecCallReceipt(
109
+ ctx, client, echo.SRPCEchoerServiceID, "Echo",
110
+ &echo.EchoMsg{Body: bodyTxt}, out,
111
+ )
112
+ if err != nil {
113
+ return fmt.Errorf("receipt call: %w", err)
114
+ }
115
+ if out.GetBody() != bodyTxt {
116
+ return fmt.Errorf("expected %q got %q", bodyTxt, out.GetBody())
117
+ }
118
+
119
+ switch receiptCase {
120
+ case "commit":
121
+ if err := receipt.Commit(); err != nil {
122
+ return fmt.Errorf("receipt commit: %w", err)
123
+ }
124
+ case "abort":
125
+ if err := receipt.Abort(); err != nil {
126
+ return fmt.Errorf("receipt abort: %w", err)
127
+ }
128
+ case "loss":
129
+ if *conn == nil {
130
+ return errors.New("receipt connection is nil")
131
+ }
132
+ tcpConn, ok := (*conn).(*net.TCPConn)
133
+ if !ok {
134
+ return errors.New("receipt connection is not TCP")
135
+ }
136
+ if err := tcpConn.SetLinger(0); err != nil {
137
+ return fmt.Errorf("set receipt loss linger: %w", err)
138
+ }
139
+ if err := tcpConn.Close(); err != nil {
140
+ return fmt.Errorf("close lost connection: %w", err)
141
+ }
142
+ if err := receipt.Commit(); err == nil {
143
+ return errors.New("loss receipt commit unexpectedly succeeded")
144
+ }
145
+ case "bare-close":
146
+ if *conn == nil {
147
+ return errors.New("receipt connection is nil")
148
+ }
149
+ tcpConn, ok := (*conn).(*net.TCPConn)
150
+ if !ok {
151
+ return errors.New("receipt connection is not TCP")
152
+ }
153
+ if err := tcpConn.CloseWrite(); err != nil {
154
+ return fmt.Errorf("close receipt write side: %w", err)
155
+ }
156
+ if err := receipt.Commit(); err == nil {
157
+ return errors.New("bare-close receipt commit unexpectedly succeeded")
158
+ }
159
+ default:
160
+ return fmt.Errorf("unknown receipt case: %s", receiptCase)
161
+ }
162
+
163
+ if err := emitReceiptEvent(
164
+ fmt.Sprintf("CLIENT_RECEIPT_RESOLVED %s", receiptTerminalName(receiptCase)),
165
+ ); err != nil {
166
+ return fmt.Errorf("record receipt resolution: %w", err)
167
+ }
168
+
169
+ fmt.Println(" PASSED")
170
+ return nil
171
+ }
172
+
73
173
  func testServerStream(ctx context.Context, client echo.SRPCEchoerClient) error {
74
174
  fmt.Println("Testing ServerStream RPC...")
75
175
  strm, err := client.EchoServerStream(ctx, &echo.EchoMsg{Body: bodyTxt})
@@ -149,3 +249,35 @@ func testBidiStream(ctx context.Context, client echo.SRPCEchoerClient) error {
149
249
  fmt.Println(" PASSED")
150
250
  return nil
151
251
  }
252
+
253
+ func receiptTerminalName(receiptCase string) string {
254
+ switch receiptCase {
255
+ case "commit":
256
+ return "committed"
257
+ case "abort":
258
+ return "canceled"
259
+ case "loss":
260
+ return "transportLost"
261
+ case "bare-close":
262
+ return "closed"
263
+ default:
264
+ return "unknown"
265
+ }
266
+ }
267
+
268
+ func emitReceiptEvent(line string) error {
269
+ fmt.Println(line)
270
+ fifo := os.Getenv("RECEIPT_EVENT_FIFO")
271
+ if fifo == "" {
272
+ return nil
273
+ }
274
+ file, err := os.OpenFile(fifo, os.O_WRONLY, 0) //nolint:gosec // FIFO path is created by the local integration runner.
275
+ if err != nil {
276
+ return err
277
+ }
278
+ if _, err := file.WriteString(line + "\n"); err != nil {
279
+ _ = file.Close()
280
+ return err
281
+ }
282
+ return file.Close()
283
+ }