starpc 0.49.20 → 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 (44) hide show
  1. package/dist/integration/cross-language/ts-server.js +22 -6
  2. package/dist/mock/index.d.ts +2 -2
  3. package/dist/mock/index.js +2 -2
  4. package/dist/rpcstream/receipt.test.d.ts +1 -0
  5. package/dist/rpcstream/receipt.test.js +41 -0
  6. package/dist/srpc/call-receipt.js +2 -1
  7. package/dist/srpc/call-receipt.test.js +15 -14
  8. package/dist/srpc/common-rpc.d.ts +1 -3
  9. package/dist/srpc/common-rpc.js +10 -13
  10. package/dist/srpc/index.d.ts +1 -1
  11. package/dist/srpc/index.js +1 -0
  12. package/dist/srpc/rpcproto.pb.d.ts +44 -0
  13. package/dist/srpc/rpcproto.pb.js +53 -0
  14. package/dist/srpc/server-invocation.d.ts +1 -1
  15. package/go.mod +13 -12
  16. package/go.sum +24 -24
  17. package/integration/cross-language/go-server/fixture-owner_test.go +127 -0
  18. package/integration/cross-language/go-server/main.go +64 -42
  19. package/integration/cross-language/run.bash +2 -2
  20. package/integration/cross-language/ts-server.ts +27 -7
  21. package/mock/index.ts +2 -2
  22. package/package.json +19 -26
  23. package/srpc/accept.go +1 -1
  24. package/srpc/call-receipt-e2e_test.go +2 -2
  25. package/srpc/call-receipt.go +1 -1
  26. package/srpc/call-receipt.test.ts +19 -16
  27. package/srpc/call-receipt.ts +2 -1
  28. package/srpc/call-receipt_test.go +19 -19
  29. package/srpc/common-rpc.go +6 -6
  30. package/srpc/common-rpc.ts +17 -16
  31. package/srpc/index.ts +1 -1
  32. package/srpc/muxed-conn.go +1 -1
  33. package/srpc/muxed-yamux.go +1 -1
  34. package/srpc/rpcproto.pb.cc +15 -4
  35. package/srpc/rpcproto.pb.go +96 -0
  36. package/srpc/rpcproto.pb.h +59 -0
  37. package/srpc/rpcproto.pb.rs +45 -0
  38. package/srpc/rpcproto.pb.ts +62 -0
  39. package/srpc/rpcproto.proto +16 -0
  40. package/srpc/schema-ownership_test.go +115 -0
  41. package/srpc/server-invocation.go +1 -19
  42. package/srpc/server-invocation.ts +1 -7
  43. package/srpc/stream-yamux.go +1 -1
  44. package/srpc/websocket.go +1 -1
@@ -0,0 +1,127 @@
1
+ package main
2
+
3
+ import (
4
+ "go/ast"
5
+ "go/parser"
6
+ "go/token"
7
+ "os"
8
+ "strconv"
9
+ "testing"
10
+ )
11
+
12
+ // forbiddenFixtureCalls names the pipeline constructors that rebuild a second
13
+ // srpc server inside a fixture. The receipt post-mortem removed a hand-built
14
+ // ReadPump pipeline; the fixture must drive one srpc.Server through one
15
+ // Server.HandleStream and observe framed packets by decorating net.Conn.
16
+ var forbiddenFixtureCalls = []string{"NewServerRPC", "NewPacketReadWriter", "ReadPump"}
17
+
18
+ // fixtureOwnerViolations returns the ways a cross-language Go fixture source
19
+ // diverges from the single production server owner.
20
+ func fixtureOwnerViolations(src string) []string {
21
+ fset := token.NewFileSet()
22
+ file, err := parser.ParseFile(fset, "fixture.go", src, 0)
23
+ if err != nil {
24
+ return []string{"parse error: " + err.Error()}
25
+ }
26
+
27
+ seen := map[string]bool{}
28
+ handleStreamCalls := 0
29
+ newServerCalls := 0
30
+ ast.Inspect(file, func(n ast.Node) bool {
31
+ call, ok := n.(*ast.CallExpr)
32
+ if !ok {
33
+ return true
34
+ }
35
+ sel, ok := call.Fun.(*ast.SelectorExpr)
36
+ if !ok {
37
+ return true
38
+ }
39
+ switch sel.Sel.Name {
40
+ case "HandleStream":
41
+ handleStreamCalls++
42
+ case "NewServer":
43
+ newServerCalls++
44
+ case "NewServerRPC", "NewPacketReadWriter", "ReadPump":
45
+ seen[sel.Sel.Name] = true
46
+ }
47
+ return true
48
+ })
49
+
50
+ var violations []string
51
+ for _, name := range forbiddenFixtureCalls {
52
+ if seen[name] {
53
+ violations = append(violations, "reconstructs server pipeline via "+name)
54
+ }
55
+ }
56
+ if newServerCalls != 1 {
57
+ violations = append(violations, "expected exactly one srpc.NewServer, found "+strconv.Itoa(newServerCalls))
58
+ }
59
+ if handleStreamCalls != 1 {
60
+ violations = append(violations, "expected exactly one Server.HandleStream entrypoint, found "+strconv.Itoa(handleStreamCalls))
61
+ }
62
+ if !decoratesNetConn(file) {
63
+ violations = append(violations, "no fixture type decorates net.Conn")
64
+ }
65
+ return violations
66
+ }
67
+
68
+ // decoratesNetConn reports whether the source embeds net.Conn in a struct, the
69
+ // only sanctioned way for fixture instrumentation to observe framed packets.
70
+ func decoratesNetConn(file *ast.File) bool {
71
+ found := false
72
+ ast.Inspect(file, func(n ast.Node) bool {
73
+ st, ok := n.(*ast.StructType)
74
+ if !ok {
75
+ return true
76
+ }
77
+ for _, field := range st.Fields.List {
78
+ if len(field.Names) != 0 {
79
+ continue
80
+ }
81
+ sel, ok := field.Type.(*ast.SelectorExpr)
82
+ if !ok {
83
+ continue
84
+ }
85
+ pkg, ok := sel.X.(*ast.Ident)
86
+ if ok && pkg.Name == "net" && sel.Sel.Name == "Conn" {
87
+ found = true
88
+ }
89
+ }
90
+ return true
91
+ })
92
+ return found
93
+ }
94
+
95
+ // TestFixtureUsesSingleServerOwner proves the shipped fixture drives one
96
+ // srpc.Server through one Server.HandleStream and decorates net.Conn.
97
+ func TestFixtureUsesSingleServerOwner(t *testing.T) {
98
+ src, err := os.ReadFile("main.go")
99
+ if err != nil {
100
+ t.Fatalf("read main.go: %v", err)
101
+ }
102
+ if violations := fixtureOwnerViolations(string(src)); len(violations) != 0 {
103
+ t.Fatalf("fixture diverged from single server owner: %v", violations)
104
+ }
105
+ }
106
+
107
+ // TestFixtureGuardDetectsSecondPipeline proves the guard fires when a fixture
108
+ // rebuilds a second RPC server pipeline.
109
+ func TestFixtureGuardDetectsSecondPipeline(t *testing.T) {
110
+ bad := `package main
111
+
112
+ import (
113
+ "net"
114
+
115
+ "github.com/aperturerobotics/starpc/srpc"
116
+ )
117
+
118
+ func run(conn net.Conn) {
119
+ prw := srpc.NewPacketReadWriter(conn)
120
+ rpc := srpc.NewServerRPC(nil, nil)
121
+ _ = rpc.ReadPump(prw)
122
+ }
123
+ `
124
+ if violations := fixtureOwnerViolations(bad); len(violations) == 0 {
125
+ t.Fatal("guard failed to flag a second server pipeline")
126
+ }
127
+ }
@@ -2,11 +2,13 @@ package main
2
2
 
3
3
  import (
4
4
  "context"
5
+ "encoding/binary"
5
6
  "fmt"
6
7
  "net"
7
8
  "os"
8
9
  "os/signal"
9
10
  "sync"
11
+ "sync/atomic"
10
12
 
11
13
  "github.com/aperturerobotics/starpc/echo"
12
14
  "github.com/aperturerobotics/starpc/srpc"
@@ -39,6 +41,7 @@ func main() {
39
41
  }
40
42
  var receiptDone <-chan struct{}
41
43
  var finishReceipt func()
44
+ var receiptCommitted atomic.Bool
42
45
  if receiptMode {
43
46
  done := make(chan struct{})
44
47
  var doneOnce sync.Once
@@ -73,16 +76,16 @@ func main() {
73
76
  if markerErr != nil {
74
77
  return true, markerErr
75
78
  }
76
- if kind != srpc.TerminalCommitted {
79
+ if kind == srpc.TerminalKind_TERMINAL_KIND_COMMITTED {
80
+ receiptCommitted.Store(true)
81
+ }
82
+ if kind != srpc.TerminalKind_TERMINAL_KIND_COMMITTED {
77
83
  finishReceipt()
78
84
  }
79
85
  return true, nil
80
86
  })
81
87
  }
82
- var server *srpc.Server
83
- if !receiptMode {
84
- server = srpc.NewServer(invoker)
85
- }
88
+ server := srpc.NewServer(invoker)
86
89
  ln, err := net.Listen("tcp", "127.0.0.1:0")
87
90
  if err != nil {
88
91
  fmt.Fprintf(os.Stderr, "listen error: %v\n", err)
@@ -101,7 +104,7 @@ func main() {
101
104
 
102
105
  go func() {
103
106
  <-ctx.Done()
104
- ln.Close()
107
+ _ = ln.Close()
105
108
  }()
106
109
 
107
110
  for {
@@ -109,63 +112,82 @@ func main() {
109
112
  if err != nil {
110
113
  return
111
114
  }
115
+ var stream net.Conn
116
+ stream = conn
112
117
  if receiptMode {
113
- go handleReceiptStream(ctx, conn, invoker, finishReceipt)
114
- } else {
115
- go server.HandleStream(ctx, conn)
118
+ stream = &receiptConn{
119
+ Conn: conn,
120
+ finishReceipt: finishReceipt,
121
+ committed: &receiptCommitted,
122
+ }
116
123
  }
124
+ go server.HandleStream(ctx, stream)
117
125
  }
118
126
  }
119
127
 
120
- func handleReceiptStream(
121
- ctx context.Context,
122
- conn net.Conn,
123
- invoker srpc.Invoker,
124
- finishReceipt func(),
125
- ) {
126
- prw := srpc.NewPacketReadWriter(conn)
127
- writer := &receiptPacketWriter{
128
- inner: prw,
129
- finishReceipt: finishReceipt,
130
- }
131
- rpc := srpc.NewServerRPC(ctx, invoker, writer)
132
- prw.ReadPump(rpc.HandlePacketData, rpc.HandleStreamClose)
133
- }
134
-
135
- type receiptPacketWriter struct {
136
- inner srpc.PacketWriter
128
+ type receiptConn struct {
129
+ net.Conn
137
130
  finishReceipt func()
131
+ committed *atomic.Bool
132
+ inspect []byte
133
+ ackPending bool
138
134
  }
139
135
 
140
- func (w *receiptPacketWriter) WritePacket(pkt *srpc.Packet) error {
141
- if err := w.inner.WritePacket(pkt); err != nil {
142
- return err
136
+ func (c *receiptConn) Write(p []byte) (int, error) {
137
+ if !c.ackPending {
138
+ if err := c.observeReceiptPackets(p); err != nil {
139
+ return 0, err
140
+ }
141
+ }
142
+ n, err := c.Conn.Write(p)
143
+ if err != nil {
144
+ return n, err
143
145
  }
144
- data := pkt.GetCallData()
145
- if data != nil && data.GetComplete() && data.GetError() == "" {
146
- if err := emitReceiptEvent("SERVER_RECEIPT_ACK committed"); err != nil {
146
+ if c.ackPending && n == len(p) {
147
+ c.finishReceipt()
148
+ c.ackPending = false
149
+ }
150
+ return n, nil
151
+ }
152
+
153
+ func (c *receiptConn) observeReceiptPackets(p []byte) error {
154
+ c.inspect = append(c.inspect, p...)
155
+ for len(c.inspect) >= 4 {
156
+ size := int(binary.LittleEndian.Uint32(c.inspect[:4]))
157
+ if len(c.inspect) < 4+size {
158
+ break
159
+ }
160
+ pkt := &srpc.Packet{}
161
+ if err := pkt.UnmarshalVT(c.inspect[4 : 4+size]); err != nil {
162
+ return err
163
+ }
164
+ c.inspect = c.inspect[4+size:]
165
+ data := pkt.GetCallData()
166
+ if !c.committed.Load() {
167
+ continue
168
+ }
169
+ if data == nil || !data.GetComplete() || data.GetError() != "" {
170
+ continue
171
+ }
172
+ if err := emitReceiptEvent("SERVER_RECEIPT_ACK_WRITE committed"); err != nil {
147
173
  return err
148
174
  }
149
- w.finishReceipt()
175
+ c.ackPending = true
150
176
  }
151
177
  return nil
152
178
  }
153
179
 
154
- func (w *receiptPacketWriter) Close() error {
155
- return w.inner.Close()
156
- }
157
-
158
180
  func terminalName(kind srpc.TerminalKind) string {
159
181
  switch kind {
160
- case srpc.TerminalCommitted:
182
+ case srpc.TerminalKind_TERMINAL_KIND_COMMITTED:
161
183
  return "committed"
162
- case srpc.TerminalCanceled:
184
+ case srpc.TerminalKind_TERMINAL_KIND_CANCELED:
163
185
  return "canceled"
164
- case srpc.TerminalLost:
186
+ case srpc.TerminalKind_TERMINAL_KIND_TRANSPORT_LOST:
165
187
  return "transportLost"
166
- case srpc.TerminalClosed:
188
+ case srpc.TerminalKind_TERMINAL_KIND_CLOSED:
167
189
  return "closed"
168
- case srpc.TerminalAbandoned:
190
+ case srpc.TerminalKind_TERMINAL_KIND_ABANDONED:
169
191
  return "abandoned"
170
192
  default:
171
193
  return "unknown"
@@ -118,14 +118,14 @@ check_receipt_events() {
118
118
  local event_log="$2"
119
119
  awk -v expected="$expected" '
120
120
  $0 == "SERVER_RECEIPT_TERMINAL " expected { terminal = NR; next }
121
- $0 == "SERVER_RECEIPT_ACK " expected { ack = NR; next }
121
+ $0 == "SERVER_RECEIPT_ACK_WRITE " expected { ack_write = NR; next }
122
122
  $0 == "CLIENT_RECEIPT_RESOLVED " expected { client = NR; next }
123
123
  END {
124
124
  if (!terminal || !client) {
125
125
  exit 1
126
126
  }
127
127
  if (expected == "committed" &&
128
- (!ack || terminal >= ack || ack >= client)) {
128
+ (!ack_write || terminal >= ack_write || ack_write >= client)) {
129
129
  exit 1
130
130
  }
131
131
  }
@@ -2,6 +2,8 @@ import net from 'net'
2
2
  import { closeSync, openSync, writeSync } from 'node:fs'
3
3
  import { pipe } from 'it-pipe'
4
4
  import { pushable } from 'it-pushable'
5
+ import type { Source } from 'it-stream-types'
6
+
5
7
  import { createMux, createHandler, Server } from '../../srpc/index.js'
6
8
  import {
7
9
  parseLengthPrefixTransform,
@@ -10,9 +12,9 @@ import {
10
12
  import { combineUint8ArrayListTransform } from '../../srpc/array-list.js'
11
13
  import { EchoerServer, EchoMsg } from '../../echo/index.js'
12
14
  import { EchoerDefinition } from '../../echo/echo_srpc.pb.js'
13
- import { Packet } from '../../srpc/rpcproto.pb.js'
15
+ import { Packet, TerminalKind } from '../../srpc/rpcproto.pb.js'
14
16
  import type { PacketStream } from '../../srpc/stream.js'
15
- import type { Source } from 'it-stream-types'
17
+
16
18
  function emitReceiptEvent(line: string): void {
17
19
  console.log(line)
18
20
  const fifo = process.env.RECEIPT_EVENT_FIFO
@@ -57,15 +59,15 @@ function tcpSocketToPacketStream(socket: net.Socket): PacketStream {
57
59
  packet.body?.case === 'callData' &&
58
60
  packet.body.value.complete &&
59
61
  !packet.body.value.error
62
+ if (receiptCompletion) {
63
+ emitReceiptEvent('SERVER_RECEIPT_ACK_WRITE committed')
64
+ }
60
65
  const writeDone = new Promise<void>((resolve, reject) => {
61
66
  socket.write(data, (err) => {
62
67
  if (err) reject(err)
63
68
  else resolve()
64
69
  })
65
70
  })
66
- if (receiptCompletion) {
67
- emitReceiptEvent('SERVER_RECEIPT_ACK committed')
68
- }
69
71
  await writeDone
70
72
  if (receiptCompletion) {
71
73
  finishReceiptServer()
@@ -121,8 +123,8 @@ if (receiptMode) {
121
123
  const terminal = await invocation.waitTerminal(
122
124
  new AbortController().signal,
123
125
  )
124
- emitReceiptEvent(`SERVER_RECEIPT_TERMINAL ${terminal}`)
125
- if (terminal !== 'committed') {
126
+ emitReceiptEvent(`SERVER_RECEIPT_TERMINAL ${terminalName(terminal)}`)
127
+ if (terminal !== TerminalKind.COMMITTED) {
126
128
  finishReceiptServer()
127
129
  }
128
130
  })(),
@@ -130,6 +132,24 @@ if (receiptMode) {
130
132
  }
131
133
  })
132
134
  }
135
+
136
+ function terminalName(terminal: TerminalKind): string {
137
+ switch (terminal) {
138
+ case TerminalKind.COMMITTED:
139
+ return 'committed'
140
+ case TerminalKind.CANCELED:
141
+ return 'canceled'
142
+ case TerminalKind.TRANSPORT_LOST:
143
+ return 'transportLost'
144
+ case TerminalKind.CLOSED:
145
+ return 'closed'
146
+ case TerminalKind.ABANDONED:
147
+ return 'abandoned'
148
+ default:
149
+ return 'unknown'
150
+ }
151
+ }
152
+
133
153
  const server = new Server(mux.lookupMethod)
134
154
  if (!receiptMode) {
135
155
  const echoer = new EchoerServer(server)
package/mock/index.ts CHANGED
@@ -1,2 +1,2 @@
1
- export * from "./mock.pb.js"
2
- export * from "./mock_srpc.pb.js"
1
+ export * from './mock.pb.js'
2
+ export * from './mock_srpc.pb.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.49.20",
3
+ "version": "0.50.0",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -68,10 +68,10 @@
68
68
  "deps": "depcheck --ignores 'bufferutil,utf-8-validate,rimraf,starpc,@aptre/protobuf-es-lite,tsx'",
69
69
  "codegen": "bun run gen",
70
70
  "ci": "bun run build && bun run lint:js && bun run lint:go",
71
- "format": "bun run format:go && bun run format:js && bun run format:config",
72
- "format:config": "prettier --write tsconfig.json package.json",
71
+ "format": "bun run format:js && bun run format:go && bun run format:config",
72
+ "format:config": "oxfmt '*.json' '.github/**/*.json' '.github/**/*.yml' '.oxfmtrc.json' 'tsconfig.json'",
73
73
  "format:go": "bun run go:aptre -- format",
74
- "format:js": "prettier --write './{srpc,echo,e2e,integration,rpcstream,cmd}/**/(*.ts|*.tsx|*.html|*.css)'",
74
+ "format:js": "oxfmt './{srpc,echo,e2e,integration,rpcstream,cmd,mock,scripts}/**/*.{ts,tsx,js,mjs,html,css,scss}' '*.{ts,tsx,js,mjs}'",
75
75
  "gen": "bun run go:aptre -- generate && bun run format",
76
76
  "gen:force": "bun run go:aptre -- generate --force && bun run format",
77
77
  "test": "bun run test:js && bun run test:go",
@@ -90,46 +90,39 @@
90
90
  "integration:rust:cpp": "bash ./integration/cross-language/run.bash rust:cpp",
91
91
  "lint": "bun run lint:go && bun run lint:js",
92
92
  "lint:go": "bun run go:aptre -- lint",
93
- "lint:js": "eslint ./",
93
+ "lint:js": "oxlint .",
94
+ "lint:js:fix": "bun run lint:js -- --fix",
94
95
  "prepare": "husky && go mod vendor",
95
96
  "go:aptre": "go run -mod=mod github.com/aperturerobotics/common/cmd/aptre",
96
97
  "precommit": "lint-staged",
97
- "release": "npm run release:version && npm run release:commit",
98
- "release:minor": "npm run release:version:minor && npm run release:commit",
99
- "release:version": "npm version patch -m \"release: v%s\" --no-git-tag-version && npm run release:sync-cargo",
100
- "release:version:minor": "npm version minor -m \"release: v%s\" --no-git-tag-version && npm run release:sync-cargo",
101
- "release:sync-cargo": "node -e \"const v=require('./package.json').version,f='Cargo.toml',c=require('fs').readFileSync(f,'utf8');require('fs').writeFileSync(f,c.replace(/^version = \\\".*?\\\"/m,'version = \\\"'+v+'\\\"'))\"",
102
- "release:commit": "git reset && git add package.json Cargo.toml && git commit -s -m \"release: v$(node -p \"require('./package.json').version\")\" && git tag v$(node -p \"require('./package.json').version\")",
98
+ "release": "bun run release:version && bun run release:commit",
99
+ "release:minor": "bun run release:version:minor && bun run release:commit",
100
+ "release:version": "bun scripts/release-version.ts patch",
101
+ "release:version:minor": "bun scripts/release-version.ts minor",
102
+ "release:commit": "version=$(bun -e 'console.log(require(\"./package.json\").version)') && git reset && git add package.json Cargo.toml && git commit -s -m \"release: v$version\" && git tag \"v$version\"",
103
103
  "release:publish": "git push && git push --tags"
104
104
  },
105
105
  "preferUnplugged": true,
106
106
  "lint-staged": {
107
- "package.json": "prettier --config .prettierrc.yaml --write",
108
- "./{srpc,echo,e2e,integration,rpcstream,cmd}/**/(*.ts|*.tsx|*.html|*.css)": "prettier --config .prettierrc.yaml --write"
107
+ "*.{json,ts,tsx,js,mjs}": "oxfmt",
108
+ "./{srpc,echo,e2e,integration,rpcstream,cmd,mock,scripts}/**/*.{ts,tsx,js,mjs,html,css,scss}": "oxfmt"
109
109
  },
110
110
  "devDependencies": {
111
- "@eslint/js": "^10.0.0",
112
- "@typescript-eslint/eslint-plugin": "^8.59.0",
113
- "@typescript-eslint/parser": "^8.59.0",
111
+ "@typescript/native-preview": "^7.0.0-dev.20260422.1",
114
112
  "depcheck": "^1.4.6",
115
113
  "esbuild": "^0.28.0",
116
- "eslint": "^10.2.1",
117
- "eslint-config-prettier": "^10.0.0",
118
- "eslint-plugin-unused-imports": "^4.4.1",
119
- "globals": "^17.5.0",
120
114
  "happy-dom": "^20.9.0",
121
115
  "husky": "^9.1.7",
122
116
  "lint-staged": "^17.0.0",
123
- "prettier": "^3.8.3",
117
+ "oxfmt": "0.61.0",
118
+ "oxlint": "^1.76.0",
124
119
  "rimraf": "^6.1.3",
125
120
  "tsx": "^4.20.4",
126
- "typescript": "^6.0.3",
127
- "@typescript/native-preview": "^7.0.0-dev.20260422.1",
128
121
  "vitest": "^4.1.5"
129
122
  },
130
123
  "dependencies": {
131
124
  "@aptre/it-ws": "^1.1.2",
132
- "@aptre/protobuf-es-lite": "1.1.0",
125
+ "@aptre/protobuf-es-lite": "1.1.1",
133
126
  "@aptre/yamux": "^1.0.3",
134
127
  "event-iterator": "^2.0.0",
135
128
  "isomorphic-ws": "^5.0.0",
@@ -141,9 +134,9 @@
141
134
  "ws": "^8.20.0"
142
135
  },
143
136
  "resolutions": {
144
- "@aptre/protobuf-es-lite": "1.1.0"
137
+ "@aptre/protobuf-es-lite": "1.1.1"
145
138
  },
146
139
  "overrides": {
147
- "@aptre/protobuf-es-lite": "1.1.0"
140
+ "@aptre/protobuf-es-lite": "1.1.1"
148
141
  }
149
142
  }
package/srpc/accept.go CHANGED
@@ -4,7 +4,7 @@ import (
4
4
  "context"
5
5
  "net"
6
6
 
7
- "github.com/libp2p/go-yamux/v4"
7
+ "github.com/libp2p/go-yamux/v5"
8
8
  )
9
9
 
10
10
  // AcceptMuxedListener accepts incoming connections from a net.Listener.
@@ -5,7 +5,7 @@ import (
5
5
  "net"
6
6
  "testing"
7
7
 
8
- yamux "github.com/libp2p/go-yamux/v4"
8
+ yamux "github.com/libp2p/go-yamux/v5"
9
9
 
10
10
  "github.com/aperturerobotics/starpc/echo"
11
11
  "github.com/aperturerobotics/starpc/srpc"
@@ -105,7 +105,7 @@ func TestCallReceiptGoGo(t *testing.T) {
105
105
  t.Fatal("generated handler context lost server invocation")
106
106
  }
107
107
  kind := <-terminal
108
- if kind != srpc.TerminalCommitted {
108
+ if kind != srpc.TerminalKind_TERMINAL_KIND_COMMITTED {
109
109
  t.Fatalf("terminal = %v, want committed", kind)
110
110
  }
111
111
  }
@@ -62,7 +62,7 @@ func (r *CallReceipt) Commit() error {
62
62
  receipt, ok := r.strm.(receiptTerminalStream)
63
63
  if ok {
64
64
  terminal, terminalOK := receipt.receiptTerminalKind()
65
- if terminalOK && terminal == TerminalCommitted {
65
+ if terminalOK && terminal == TerminalKind_TERMINAL_KIND_COMMITTED {
66
66
  return nil
67
67
  }
68
68
  }
@@ -9,9 +9,9 @@ import { Client } from './client.js'
9
9
  import { ClientRPC } from './client-rpc.js'
10
10
  import { Server } from './server.js'
11
11
  import { CallReceipt } from './call-receipt.js'
12
- import { Packet } from './rpcproto.pb.js'
12
+ import { Packet, TerminalKind } from './rpcproto.pb.js'
13
13
  import { ServerRPC } from './server-rpc.js'
14
- import { ServerInvocation, type TerminalKind } from './server-invocation.js'
14
+ import { ServerInvocation } from './server-invocation.js'
15
15
 
16
16
  const response = new Uint8Array([1, 2, 3])
17
17
 
@@ -68,7 +68,7 @@ describe('held unary receipt', () => {
68
68
  })
69
69
 
70
70
  it('commits through a real TypeScript server', async () => {
71
- const terminal = Promise.withResolvers<string>()
71
+ const terminal = Promise.withResolvers<TerminalKind>()
72
72
  const server = new Server(async () => {
73
73
  return async (_source, dataSink, invocation) => {
74
74
  await dataSink(
@@ -113,7 +113,7 @@ describe('held unary receipt', () => {
113
113
  response,
114
114
  )
115
115
  await expect(held.receipt.commit()).resolves.toBeUndefined()
116
- await expect(terminal.promise).resolves.toBe('committed')
116
+ await expect(terminal.promise).resolves.toBe(TerminalKind.COMMITTED)
117
117
  })
118
118
 
119
119
  it('rejects commit after a bare remote close', async () => {
@@ -292,12 +292,12 @@ describe('held unary receipt', () => {
292
292
 
293
293
  describe('server invocation terminal', () => {
294
294
  it.each([
295
- ['explicit completion', 'committed'],
296
- ['cancel', 'canceled'],
297
- ['transport loss', 'transportLost'],
298
- ['remote error packet', 'transportLost'],
299
- ['bare close', 'closed'],
300
- ['remote error packet with completion', 'transportLost'],
295
+ ['explicit completion', TerminalKind.COMMITTED],
296
+ ['cancel', TerminalKind.CANCELED],
297
+ ['transport loss', TerminalKind.TRANSPORT_LOST],
298
+ ['remote error packet', TerminalKind.TRANSPORT_LOST],
299
+ ['bare close', TerminalKind.CLOSED],
300
+ ['remote error packet with completion', TerminalKind.TRANSPORT_LOST],
301
301
  ] as const)('%s is classified distinctly', async (_name, expected) => {
302
302
  const captured = Promise.withResolvers<ServerInvocation>()
303
303
  const owner = new AbortController()
@@ -318,11 +318,11 @@ describe('server invocation terminal', () => {
318
318
  dataIsZero: true,
319
319
  })
320
320
  const invocation = await captured.promise
321
- if (expected === 'committed') {
321
+ if (expected === TerminalKind.COMMITTED) {
322
322
  await rpc.handleCallData({ complete: true })
323
- } else if (expected === 'canceled') {
323
+ } else if (expected === TerminalKind.CANCELED) {
324
324
  await rpc.handleCallCancel()
325
- } else if (expected === 'transportLost') {
325
+ } else if (expected === TerminalKind.TRANSPORT_LOST) {
326
326
  if (
327
327
  _name === 'remote error packet' ||
328
328
  _name === 'remote error packet with completion'
@@ -346,7 +346,10 @@ describe('server invocation terminal', () => {
346
346
  }
347
347
  expect(rpcState.remoteCompleted).toBe(false)
348
348
  }
349
- if (expected === 'closed' || expected === 'transportLost') {
349
+ if (
350
+ expected === TerminalKind.CLOSED ||
351
+ expected === TerminalKind.TRANSPORT_LOST
352
+ ) {
350
353
  expect(invocation.signal.aborted).toBe(true)
351
354
  }
352
355
  })
@@ -379,7 +382,7 @@ describe('server invocation terminal', () => {
379
382
  await rpc.handleCallCancel()
380
383
  }
381
384
  await expect(invocation.waitTerminal(owner.signal)).resolves.toBe(
382
- 'committed',
385
+ TerminalKind.COMMITTED,
383
386
  )
384
387
  owner.abort()
385
388
  },
@@ -406,7 +409,7 @@ describe('server invocation terminal', () => {
406
409
  const invocation = await captured.promise
407
410
  owner.abort()
408
411
  await expect(invocation.waitTerminal(owner.signal)).resolves.toBe(
409
- 'abandoned',
412
+ TerminalKind.ABANDONED,
410
413
  )
411
414
  })
412
415
  })
@@ -1,6 +1,7 @@
1
1
  /// <reference lib="es2024.promise" />
2
2
  import { ERR_RPC_ABORT } from './errors.js'
3
3
  import { ClientRPC } from './client-rpc.js'
4
+ import { TerminalKind } from './rpcproto.pb.js'
4
5
 
5
6
  // ReceiptRpc exposes held unary calls without widening ProtoRpc.
6
7
  export interface ReceiptRpc {
@@ -49,7 +50,7 @@ export class CallReceipt {
49
50
  }
50
51
  const terminal = this.#call.getTerminalKind()
51
52
  if (
52
- terminal !== 'committed' ||
53
+ terminal !== TerminalKind.COMMITTED ||
53
54
  !this.#requestCommitted ||
54
55
  this.#terminal !== 'committed'
55
56
  ) {