starpc 0.40.0 → 0.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +76 -88
- package/dist/srpc/conn.d.ts +8 -9
- package/dist/srpc/conn.js +10 -19
- package/dist/srpc/index.d.ts +0 -1
- package/dist/srpc/index.js +0 -1
- package/dist/srpc/log.d.ts +3 -0
- package/dist/srpc/log.js +22 -0
- package/dist/srpc/stream.d.ts +1 -1
- package/dist/srpc/stream.js +3 -11
- package/dist/srpc/websocket.d.ts +2 -2
- package/package.json +10 -11
- package/srpc/conn.ts +19 -39
- package/srpc/index.ts +0 -5
- package/srpc/log.ts +26 -0
- package/srpc/stream.ts +4 -11
- package/srpc/websocket.ts +2 -6
- package/dist/srpc/duplex-message-stream.d.ts +0 -22
- package/dist/srpc/duplex-message-stream.js +0 -95
- package/srpc/duplex-message-stream.ts +0 -132
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# starpc
|
|
2
2
|
|
|
3
3
|
[![GoDoc Widget]][GoDoc] [![Go Report Card Widget]][Go Report Card]
|
|
4
4
|
|
|
5
|
-
>
|
|
5
|
+
> Streaming Protobuf RPC with bidirectional streaming over any multiplexed transport.
|
|
6
6
|
|
|
7
7
|
[GoDoc]: https://godoc.org/github.com/aperturerobotics/starpc
|
|
8
8
|
[GoDoc Widget]: https://godoc.org/github.com/aperturerobotics/starpc?status.svg
|
|
@@ -18,20 +18,22 @@
|
|
|
18
18
|
- [Protobuf Definition](#protobuf)
|
|
19
19
|
- [Go Implementation](#go)
|
|
20
20
|
- [TypeScript Implementation](#typescript)
|
|
21
|
+
- [Debugging](#debugging)
|
|
21
22
|
- [Development Setup](#development-setup)
|
|
22
23
|
- [Support](#support)
|
|
23
24
|
|
|
24
25
|
## Features
|
|
25
26
|
|
|
26
|
-
- Full [Proto3 services]
|
|
27
|
-
- Bidirectional streaming
|
|
28
|
-
- Built on libp2p streams with
|
|
29
|
-
- Efficient
|
|
30
|
-
- Zero-reflection Go
|
|
31
|
-
- TypeScript
|
|
32
|
-
- Sub-
|
|
27
|
+
- Full [Proto3 services] support for TypeScript and Go
|
|
28
|
+
- Bidirectional streaming in browsers via WebSocket or WebRTC
|
|
29
|
+
- Built on libp2p streams with [@chainsafe/libp2p-yamux]
|
|
30
|
+
- Efficient multiplexing of RPCs over single connections
|
|
31
|
+
- Zero-reflection Go via [protobuf-go-lite]
|
|
32
|
+
- Lightweight TypeScript via [protobuf-es-lite]
|
|
33
|
+
- Sub-stream support via [rpcstream]
|
|
33
34
|
|
|
34
35
|
[Proto3 services]: https://developers.google.com/protocol-buffers/docs/proto3#services
|
|
36
|
+
[@chainsafe/libp2p-yamux]: https://github.com/ChainSafe/js-libp2p-yamux
|
|
35
37
|
[protobuf-go-lite]: https://github.com/aperturerobotics/protobuf-go-lite
|
|
36
38
|
[protobuf-es-lite]: https://github.com/aperturerobotics/protobuf-es-lite
|
|
37
39
|
[rpcstream]: ./rpcstream
|
|
@@ -119,118 +121,115 @@ if out.GetBody() != bodyTxt {
|
|
|
119
121
|
}
|
|
120
122
|
```
|
|
121
123
|
|
|
122
|
-
[e2e test]: ./e2e/e2e_test.go
|
|
123
|
-
|
|
124
124
|
### TypeScript
|
|
125
125
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
For an example of Go <-> TypeScript interop, see the [integration] test. For an
|
|
129
|
-
example of TypeScript <-> TypeScript interop, see the [e2e] test.
|
|
126
|
+
For Go <-> TypeScript interop, see the [integration] test.
|
|
127
|
+
For TypeScript <-> TypeScript, see the [e2e] test.
|
|
130
128
|
|
|
131
129
|
[e2e]: ./e2e/e2e.ts
|
|
132
130
|
[integration]: ./integration/integration.ts
|
|
133
131
|
|
|
134
|
-
Supports any AsyncIterable communication channel.
|
|
135
|
-
|
|
136
132
|
#### WebSocket Example
|
|
137
133
|
|
|
138
|
-
|
|
134
|
+
Connect to a WebSocket server:
|
|
139
135
|
|
|
140
136
|
```typescript
|
|
141
|
-
import { WebSocketConn } from '
|
|
142
|
-
import { EchoerClient } from '
|
|
137
|
+
import { WebSocketConn } from 'starpc'
|
|
138
|
+
import { EchoerClient } from './echo/index.js'
|
|
143
139
|
|
|
144
|
-
const ws = new WebSocket('ws://localhost:
|
|
145
|
-
const
|
|
146
|
-
const client =
|
|
147
|
-
const
|
|
140
|
+
const ws = new WebSocket('ws://localhost:8080/api')
|
|
141
|
+
const conn = new WebSocketConn(ws)
|
|
142
|
+
const client = conn.buildClient()
|
|
143
|
+
const echoer = new EchoerClient(client)
|
|
148
144
|
|
|
149
|
-
const result = await
|
|
150
|
-
|
|
151
|
-
})
|
|
152
|
-
console.log('output', result.body)
|
|
145
|
+
const result = await echoer.Echo({ body: 'Hello world!' })
|
|
146
|
+
console.log('result:', result.body)
|
|
153
147
|
```
|
|
154
148
|
|
|
155
|
-
#### In-
|
|
149
|
+
#### In-Memory Example
|
|
156
150
|
|
|
157
|
-
|
|
151
|
+
Server and client with an in-memory pipe:
|
|
158
152
|
|
|
159
153
|
```typescript
|
|
160
154
|
import { pipe } from 'it-pipe'
|
|
161
|
-
import { createHandler, createMux, Server,
|
|
162
|
-
import { EchoerDefinition, EchoerServer
|
|
163
|
-
import { pushable } from 'it-pushable'
|
|
155
|
+
import { createHandler, createMux, Server, StreamConn } from 'starpc'
|
|
156
|
+
import { EchoerDefinition, EchoerServer } from './echo/index.js'
|
|
164
157
|
|
|
165
|
-
// Create
|
|
158
|
+
// Create server with registered handlers
|
|
166
159
|
const mux = createMux()
|
|
167
160
|
const echoer = new EchoerServer()
|
|
168
161
|
mux.register(createHandler(EchoerDefinition, echoer))
|
|
169
162
|
const server = new Server(mux.lookupMethod)
|
|
170
163
|
|
|
171
|
-
// Create
|
|
172
|
-
const clientConn = new
|
|
173
|
-
const serverConn = new
|
|
164
|
+
// Create client and server connections, pipe together
|
|
165
|
+
const clientConn = new StreamConn()
|
|
166
|
+
const serverConn = new StreamConn(server)
|
|
174
167
|
pipe(clientConn, serverConn, clientConn)
|
|
175
|
-
const client = new Client(clientConn.buildOpenStreamFunc())
|
|
176
168
|
|
|
177
|
-
//
|
|
169
|
+
// Build client and make RPC calls
|
|
170
|
+
const client = clientConn.buildClient()
|
|
171
|
+
const echoerClient = new EchoerClient(client)
|
|
178
172
|
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
console.log('
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
console.log('Calling EchoServerStream: server -> client...')
|
|
196
|
-
const serverStream = demoServiceClient.EchoServerStream({
|
|
197
|
-
body: 'Hello world from server to client streaming request.',
|
|
198
|
-
})
|
|
199
|
-
for await (const msg of serverStream) {
|
|
200
|
-
console.log('server: output', msg.body)
|
|
173
|
+
// Unary call
|
|
174
|
+
const result = await echoerClient.Echo({ body: 'Hello world!' })
|
|
175
|
+
console.log('result:', result.body)
|
|
176
|
+
|
|
177
|
+
// Client streaming
|
|
178
|
+
import { pushable } from 'it-pushable'
|
|
179
|
+
const stream = pushable({ objectMode: true })
|
|
180
|
+
stream.push({ body: 'Message 1' })
|
|
181
|
+
stream.push({ body: 'Message 2' })
|
|
182
|
+
stream.end()
|
|
183
|
+
const response = await echoerClient.EchoClientStream(stream)
|
|
184
|
+
console.log('response:', response.body)
|
|
185
|
+
|
|
186
|
+
// Server streaming
|
|
187
|
+
for await (const msg of echoerClient.EchoServerStream({ body: 'Hello' })) {
|
|
188
|
+
console.log('server msg:', msg.body)
|
|
201
189
|
}
|
|
202
190
|
```
|
|
203
191
|
|
|
192
|
+
## Debugging
|
|
193
|
+
|
|
194
|
+
Enable debug logging in TypeScript using the `DEBUG` environment variable:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Enable all starpc logs
|
|
198
|
+
DEBUG=starpc:* node app.js
|
|
199
|
+
|
|
200
|
+
# Enable specific component logs
|
|
201
|
+
DEBUG=starpc:stream-conn node app.js
|
|
202
|
+
```
|
|
203
|
+
|
|
204
204
|
## Attribution
|
|
205
205
|
|
|
206
206
|
`protoc-gen-go-starpc` is a heavily modified version of `protoc-gen-go-drpc`.
|
|
207
|
-
|
|
208
|
-
Be sure to check out [drpc] as well: it's compatible with grpc, twirp, and more.
|
|
207
|
+
Check out [drpc] as well - it's compatible with grpc, twirp, and more.
|
|
209
208
|
|
|
210
209
|
[drpc]: https://github.com/storj/drpc
|
|
211
210
|
|
|
212
|
-
Uses [vtprotobuf] to generate
|
|
211
|
+
Uses [vtprotobuf] to generate Protobuf marshal/unmarshal code for Go.
|
|
213
212
|
|
|
214
213
|
[vtprotobuf]: https://github.com/planetscale/vtprotobuf
|
|
215
214
|
|
|
216
|
-
|
|
215
|
+
`protoc-gen-es-starpc` is a modified version of `protoc-gen-connect-es`.
|
|
216
|
+
Uses [protobuf-es-lite] (fork of [protobuf-es]) for TypeScript.
|
|
217
217
|
|
|
218
218
|
[protobuf-es]: https://github.com/bufbuild/protobuf-es
|
|
219
|
-
[protobuf-es-lite]: https://github.com/aperturerobotics/protobuf-es-lite
|
|
220
|
-
|
|
221
|
-
`protoc-gen-es-starpc` is a heavily modified version of `protoc-gen-connect-es`.
|
|
222
219
|
|
|
223
220
|
## Development Setup
|
|
224
221
|
|
|
225
|
-
### MacOS
|
|
222
|
+
### MacOS
|
|
223
|
+
|
|
224
|
+
Install required packages:
|
|
226
225
|
|
|
227
|
-
1. Install required packages:
|
|
228
226
|
```bash
|
|
229
227
|
brew install bash make coreutils gnu-sed findutils protobuf
|
|
230
228
|
brew link --overwrite protobuf
|
|
231
229
|
```
|
|
232
230
|
|
|
233
|
-
|
|
231
|
+
Add to your shell rc file (.bashrc, .zshrc):
|
|
232
|
+
|
|
234
233
|
```bash
|
|
235
234
|
export PATH="/opt/homebrew/opt/coreutils/libexec/gnubin:$PATH"
|
|
236
235
|
export PATH="/opt/homebrew/opt/gnu-sed/libexec/gnubin:$PATH"
|
|
@@ -238,23 +237,12 @@ export PATH="/opt/homebrew/opt/findutils/libexec/gnubin:$PATH"
|
|
|
238
237
|
export PATH="/opt/homebrew/opt/make/libexec/gnubin:$PATH"
|
|
239
238
|
```
|
|
240
239
|
|
|
241
|
-
## Attribution
|
|
242
|
-
|
|
243
|
-
- `protoc-gen-go-starpc`: Modified version of `protoc-gen-go-drpc`
|
|
244
|
-
- `protoc-gen-es-starpc`: Modified version of `protoc-gen-connect-es`
|
|
245
|
-
- Uses [vtprotobuf] for Go Protobuf marshaling
|
|
246
|
-
- Uses [protobuf-es-lite] for TypeScript Protobuf interfaces
|
|
247
|
-
|
|
248
|
-
[vtprotobuf]: https://github.com/planetscale/vtprotobuf
|
|
249
|
-
|
|
250
240
|
## Support
|
|
251
241
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
- [
|
|
255
|
-
- [Join our Discord][Join Discord]
|
|
256
|
-
- [Matrix Chat][Matrix Chat]
|
|
242
|
+
- [GitHub Issues][issues]
|
|
243
|
+
- [Discord][discord]
|
|
244
|
+
- [Matrix][matrix]
|
|
257
245
|
|
|
258
|
-
[
|
|
259
|
-
[
|
|
260
|
-
[
|
|
246
|
+
[issues]: https://github.com/aperturerobotics/starpc/issues/new
|
|
247
|
+
[discord]: https://discord.gg/KJutMESRsT
|
|
248
|
+
[matrix]: https://matrix.to/#/#aperturerobotics:matrix.org
|
package/dist/srpc/conn.d.ts
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import { YamuxMuxerInit } from '@chainsafe/libp2p-yamux';
|
|
2
|
-
import type {
|
|
3
|
-
import type { Duplex
|
|
2
|
+
import type { ComponentLogger, Direction, Stream, StreamMuxer, StreamMuxerFactory } from '@libp2p/interface';
|
|
3
|
+
import type { Duplex } from 'it-stream-types';
|
|
4
4
|
import { Uint8ArrayList } from 'uint8arraylist';
|
|
5
5
|
import { type OpenStreamFunc, type PacketStream } from './stream.js';
|
|
6
6
|
import { Client } from './client.js';
|
|
7
7
|
export interface StreamConnParams {
|
|
8
|
-
|
|
8
|
+
logger?: ComponentLogger;
|
|
9
9
|
muxerFactory?: StreamMuxerFactory;
|
|
10
|
-
direction?:
|
|
10
|
+
direction?: Direction;
|
|
11
11
|
yamuxParams?: YamuxMuxerInit;
|
|
12
12
|
}
|
|
13
13
|
export interface StreamHandler {
|
|
14
14
|
handlePacketStream(strm: PacketStream): void;
|
|
15
15
|
}
|
|
16
|
-
export declare class StreamConn implements Duplex<
|
|
16
|
+
export declare class StreamConn implements Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>> {
|
|
17
17
|
private _muxer;
|
|
18
|
-
private _messageStream;
|
|
19
18
|
private _server?;
|
|
20
19
|
constructor(server?: StreamHandler, connParams?: StreamConnParams);
|
|
21
|
-
get sink(): (
|
|
22
|
-
get source():
|
|
20
|
+
get sink(): import("it-stream-types").Sink<AsyncGenerator<Uint8Array<ArrayBufferLike> | Uint8ArrayList, any, any>, unknown>;
|
|
21
|
+
get source(): AsyncGenerator<Uint8Array<ArrayBufferLike> | Uint8ArrayList, any, any>;
|
|
23
22
|
get streams(): Stream[];
|
|
24
|
-
get muxer(): StreamMuxer
|
|
23
|
+
get muxer(): StreamMuxer;
|
|
25
24
|
get server(): StreamHandler | undefined;
|
|
26
25
|
buildClient(): Client;
|
|
27
26
|
openStream(): Promise<PacketStream>;
|
package/dist/srpc/conn.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { yamux } from '@chainsafe/libp2p-yamux';
|
|
2
2
|
import { streamToPacketStream, } from './stream.js';
|
|
3
3
|
import { Client } from './client.js';
|
|
4
|
-
import {
|
|
4
|
+
import { createDisabledComponentLogger } from './log.js';
|
|
5
5
|
// StreamConn implements a generic connection with a two-way stream.
|
|
6
6
|
// The stream is not expected to manage packet boundaries.
|
|
7
7
|
// Packets will be sent with uint32le length prefixes.
|
|
@@ -13,37 +13,28 @@ import { createDuplexMessageStream, } from './duplex-message-stream.js';
|
|
|
13
13
|
export class StreamConn {
|
|
14
14
|
// muxer is the stream muxer.
|
|
15
15
|
_muxer;
|
|
16
|
-
// messageStream wraps the duplex as a MessageStream for the muxer.
|
|
17
|
-
_messageStream;
|
|
18
16
|
// server is the server side, if set.
|
|
19
17
|
_server;
|
|
20
18
|
constructor(server, connParams) {
|
|
21
19
|
if (server) {
|
|
22
20
|
this._server = server;
|
|
23
21
|
}
|
|
24
|
-
// Create the MessageStream adapter
|
|
25
|
-
const direction = connParams?.direction || 'outbound';
|
|
26
|
-
this._messageStream = createDuplexMessageStream({
|
|
27
|
-
loggerName: connParams?.loggerName,
|
|
28
|
-
direction,
|
|
29
|
-
});
|
|
30
|
-
// Create the muxer factory - yamux(init)() returns a StreamMuxerFactory
|
|
31
22
|
const muxerFactory = connParams?.muxerFactory ??
|
|
32
|
-
yamux({ enableKeepAlive: false, ...connParams?.yamuxParams })(
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
23
|
+
yamux({ enableKeepAlive: false, ...connParams?.yamuxParams })({
|
|
24
|
+
logger: connParams?.logger ?? createDisabledComponentLogger(),
|
|
25
|
+
});
|
|
26
|
+
this._muxer = muxerFactory.createStreamMuxer({
|
|
27
|
+
onIncomingStream: this.handleIncomingStream.bind(this),
|
|
28
|
+
direction: connParams?.direction || 'outbound',
|
|
38
29
|
});
|
|
39
30
|
}
|
|
40
31
|
// sink returns the message sink.
|
|
41
32
|
get sink() {
|
|
42
|
-
return this.
|
|
33
|
+
return this._muxer.sink;
|
|
43
34
|
}
|
|
44
35
|
// source returns the outgoing message source.
|
|
45
36
|
get source() {
|
|
46
|
-
return this.
|
|
37
|
+
return this._muxer.source;
|
|
47
38
|
}
|
|
48
39
|
// streams returns the set of all ongoing streams.
|
|
49
40
|
get streams() {
|
|
@@ -63,7 +54,7 @@ export class StreamConn {
|
|
|
63
54
|
}
|
|
64
55
|
// openStream implements the client open stream function.
|
|
65
56
|
async openStream() {
|
|
66
|
-
const strm = await this.muxer.
|
|
57
|
+
const strm = await this.muxer.newStream();
|
|
67
58
|
return streamToPacketStream(strm);
|
|
68
59
|
}
|
|
69
60
|
// buildOpenStreamFunc returns openStream bound to this conn.
|
package/dist/srpc/index.d.ts
CHANGED
|
@@ -20,4 +20,3 @@ export { HandleStreamCtr } from './handle-stream-ctr.js';
|
|
|
20
20
|
export { writeToPushable, buildPushableSink, messagePushable, } from './pushable.js';
|
|
21
21
|
export { Watchdog } from './watchdog.js';
|
|
22
22
|
export { ProtoRpc } from './proto-rpc.js';
|
|
23
|
-
export { DuplexMessageStream, DuplexMessageStreamInit, createDuplexMessageStream, } from './duplex-message-stream.js';
|
package/dist/srpc/index.js
CHANGED
|
@@ -18,4 +18,3 @@ export { OpenStreamCtr } from './open-stream-ctr.js';
|
|
|
18
18
|
export { HandleStreamCtr } from './handle-stream-ctr.js';
|
|
19
19
|
export { writeToPushable, buildPushableSink, messagePushable, } from './pushable.js';
|
|
20
20
|
export { Watchdog } from './watchdog.js';
|
|
21
|
-
export { DuplexMessageStream, createDuplexMessageStream, } from './duplex-message-stream.js';
|
package/dist/srpc/log.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// https://github.com/libp2p/js-libp2p/issues/2276
|
|
2
|
+
// https://github.com/libp2p/js-libp2p/blob/bca8d6e689b47d85dda74082ed72e671139391de/packages/logger/src/index.ts#L86
|
|
3
|
+
// https://github.com/libp2p/js-libp2p/issues/2275
|
|
4
|
+
// https://github.com/ChainSafe/js-libp2p-yamux/issues/69
|
|
5
|
+
export function createDisabledLogger(namespace) {
|
|
6
|
+
const logger = () => { };
|
|
7
|
+
logger.enabled = false;
|
|
8
|
+
logger.color = '';
|
|
9
|
+
logger.diff = 0;
|
|
10
|
+
logger.log = () => { };
|
|
11
|
+
logger.namespace = namespace;
|
|
12
|
+
logger.destroy = () => true;
|
|
13
|
+
logger.extend = () => logger;
|
|
14
|
+
logger.debug = logger;
|
|
15
|
+
logger.error = logger;
|
|
16
|
+
logger.trace = logger;
|
|
17
|
+
logger.newScope = () => logger;
|
|
18
|
+
return logger;
|
|
19
|
+
}
|
|
20
|
+
export function createDisabledComponentLogger() {
|
|
21
|
+
return { forComponent: createDisabledLogger };
|
|
22
|
+
}
|
package/dist/srpc/stream.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Duplex, Source } from 'it-stream-types';
|
|
2
|
-
import
|
|
2
|
+
import { Stream } from '@libp2p/interface';
|
|
3
3
|
import type { Packet } from './rpcproto.pb.js';
|
|
4
4
|
export type PacketHandler = (packet: Packet) => Promise<void>;
|
|
5
5
|
export type PacketStream = Duplex<AsyncGenerator<Uint8Array>, Source<Uint8Array>, Promise<void>>;
|
package/dist/srpc/stream.js
CHANGED
|
@@ -8,17 +8,9 @@ export function streamToPacketStream(stream) {
|
|
|
8
8
|
return {
|
|
9
9
|
source: pipe(stream, parseLengthPrefixTransform(), combineUint8ArrayListTransform()),
|
|
10
10
|
sink: async (source) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
await stream.close();
|
|
16
|
-
}
|
|
17
|
-
catch (err) {
|
|
18
|
-
await stream
|
|
19
|
-
.close({ signal: AbortSignal.timeout(1000) })
|
|
20
|
-
.catch(() => { });
|
|
21
|
-
}
|
|
11
|
+
await pipe(source, prependLengthPrefixTransform(), stream)
|
|
12
|
+
.catch((err) => stream.close(err))
|
|
13
|
+
.then(() => stream.close());
|
|
22
14
|
},
|
|
23
15
|
};
|
|
24
16
|
}
|
package/dist/srpc/websocket.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Direction } from '@libp2p/interface';
|
|
2
2
|
import type WebSocket from '@aptre/it-ws/web-socket';
|
|
3
3
|
import { StreamConn } from './conn.js';
|
|
4
4
|
import { Server } from './server.js';
|
|
5
5
|
export declare class WebSocketConn extends StreamConn {
|
|
6
6
|
private socket;
|
|
7
|
-
constructor(socket: WebSocket, direction:
|
|
7
|
+
constructor(socket: WebSocket, direction: Direction, server?: Server);
|
|
8
8
|
getSocket(): WebSocket;
|
|
9
9
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.0",
|
|
4
4
|
"description": "Streaming protobuf RPC service protocol over any two-way channel.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -85,27 +85,26 @@
|
|
|
85
85
|
"./{srpc,echo,e2e,integration,rpcstream,cmd}/**/(*.ts|*.tsx|*.html|*.css)": "prettier --config .prettierrc.yaml --write"
|
|
86
86
|
},
|
|
87
87
|
"devDependencies": {
|
|
88
|
-
"@typescript-eslint/eslint-plugin": "^8.
|
|
89
|
-
"@typescript-eslint/parser": "^8.
|
|
88
|
+
"@typescript-eslint/eslint-plugin": "^8.48.0",
|
|
89
|
+
"@typescript-eslint/parser": "^8.48.0",
|
|
90
90
|
"depcheck": "^1.4.6",
|
|
91
91
|
"esbuild": "^0.27.0",
|
|
92
|
-
"eslint": "^9.
|
|
92
|
+
"eslint": "^9.39.1",
|
|
93
93
|
"eslint-config-prettier": "^10.0.2",
|
|
94
|
+
"happy-dom": "^20.0.10",
|
|
94
95
|
"husky": "^9.1.7",
|
|
95
|
-
"lint-staged": "^16.
|
|
96
|
+
"lint-staged": "^16.2.7",
|
|
96
97
|
"prettier": "^3.5.3",
|
|
97
|
-
"rimraf": "^6.
|
|
98
|
+
"rimraf": "^6.1.2",
|
|
98
99
|
"tsx": "^4.20.4",
|
|
99
100
|
"typescript": "^5.8.2",
|
|
100
|
-
"vitest": "^4.0.
|
|
101
|
+
"vitest": "^4.0.14"
|
|
101
102
|
},
|
|
102
103
|
"dependencies": {
|
|
103
104
|
"@aptre/it-ws": "^1.1.2",
|
|
104
105
|
"@aptre/protobuf-es-lite": "^0.5.2",
|
|
105
|
-
"@chainsafe/libp2p-yamux": "^
|
|
106
|
-
"@libp2p/interface": "^
|
|
107
|
-
"@libp2p/logger": "^6.2.2",
|
|
108
|
-
"@libp2p/utils": "^7.0.9",
|
|
106
|
+
"@chainsafe/libp2p-yamux": "^7.0.1",
|
|
107
|
+
"@libp2p/interface": "^2.6.1",
|
|
109
108
|
"event-iterator": "^2.0.0",
|
|
110
109
|
"isomorphic-ws": "^5.0.0",
|
|
111
110
|
"it-first": "^3.0.6",
|
package/srpc/conn.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { YamuxMuxerInit, yamux } from '@chainsafe/libp2p-yamux'
|
|
2
2
|
import type {
|
|
3
|
-
|
|
3
|
+
ComponentLogger,
|
|
4
|
+
Direction,
|
|
4
5
|
Stream,
|
|
5
6
|
StreamMuxer,
|
|
6
7
|
StreamMuxerFactory,
|
|
7
8
|
} from '@libp2p/interface'
|
|
8
|
-
import type { Duplex
|
|
9
|
+
import type { Duplex } from 'it-stream-types'
|
|
9
10
|
import { Uint8ArrayList } from 'uint8arraylist'
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -14,20 +15,17 @@ import {
|
|
|
14
15
|
type PacketStream,
|
|
15
16
|
} from './stream.js'
|
|
16
17
|
import { Client } from './client.js'
|
|
17
|
-
import {
|
|
18
|
-
DuplexMessageStream,
|
|
19
|
-
createDuplexMessageStream,
|
|
20
|
-
} from './duplex-message-stream.js'
|
|
18
|
+
import { createDisabledComponentLogger } from './log.js'
|
|
21
19
|
|
|
22
20
|
// ConnParams are parameters that can be passed to the StreamConn constructor.
|
|
23
21
|
export interface StreamConnParams {
|
|
24
|
-
//
|
|
25
|
-
|
|
22
|
+
// logger is the logger to use, defaults to disabled logger.
|
|
23
|
+
logger?: ComponentLogger
|
|
26
24
|
// muxerFactory overrides using the default yamux factory.
|
|
27
25
|
muxerFactory?: StreamMuxerFactory
|
|
28
26
|
// direction is the muxer connection direction.
|
|
29
27
|
// defaults to outbound (client).
|
|
30
|
-
direction?:
|
|
28
|
+
direction?: Direction
|
|
31
29
|
// yamuxParams are parameters to pass to yamux.
|
|
32
30
|
// only used if muxerFactory is unset
|
|
33
31
|
yamuxParams?: YamuxMuxerInit
|
|
@@ -50,17 +48,10 @@ export interface StreamHandler {
|
|
|
50
48
|
// Implements the server by handling incoming streams.
|
|
51
49
|
// If the server is unset, rejects any incoming streams.
|
|
52
50
|
export class StreamConn
|
|
53
|
-
implements
|
|
54
|
-
Duplex<
|
|
55
|
-
AsyncIterable<Uint8Array | Uint8ArrayList>,
|
|
56
|
-
Source<Uint8Array | Uint8ArrayList>,
|
|
57
|
-
Promise<void>
|
|
58
|
-
>
|
|
51
|
+
implements Duplex<AsyncGenerator<Uint8Array | Uint8ArrayList>>
|
|
59
52
|
{
|
|
60
53
|
// muxer is the stream muxer.
|
|
61
54
|
private _muxer: StreamMuxer
|
|
62
|
-
// messageStream wraps the duplex as a MessageStream for the muxer.
|
|
63
|
-
private _messageStream: DuplexMessageStream
|
|
64
55
|
// server is the server side, if set.
|
|
65
56
|
private _server?: StreamHandler
|
|
66
57
|
|
|
@@ -68,36 +59,25 @@ export class StreamConn
|
|
|
68
59
|
if (server) {
|
|
69
60
|
this._server = server
|
|
70
61
|
}
|
|
71
|
-
|
|
72
|
-
// Create the MessageStream adapter
|
|
73
|
-
const direction = connParams?.direction || 'outbound'
|
|
74
|
-
this._messageStream = createDuplexMessageStream({
|
|
75
|
-
loggerName: connParams?.loggerName,
|
|
76
|
-
direction,
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
// Create the muxer factory - yamux(init)() returns a StreamMuxerFactory
|
|
80
62
|
const muxerFactory =
|
|
81
63
|
connParams?.muxerFactory ??
|
|
82
|
-
yamux({ enableKeepAlive: false, ...connParams?.yamuxParams })(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
this._muxer = muxerFactory.createStreamMuxer(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
this._muxer.addEventListener('stream', (evt) => {
|
|
89
|
-
this.handleIncomingStream(evt.detail)
|
|
64
|
+
yamux({ enableKeepAlive: false, ...connParams?.yamuxParams })({
|
|
65
|
+
logger: connParams?.logger ?? createDisabledComponentLogger(),
|
|
66
|
+
})
|
|
67
|
+
this._muxer = muxerFactory.createStreamMuxer({
|
|
68
|
+
onIncomingStream: this.handleIncomingStream.bind(this),
|
|
69
|
+
direction: connParams?.direction || 'outbound',
|
|
90
70
|
})
|
|
91
71
|
}
|
|
92
72
|
|
|
93
73
|
// sink returns the message sink.
|
|
94
|
-
get sink()
|
|
95
|
-
return this.
|
|
74
|
+
get sink() {
|
|
75
|
+
return this._muxer.sink
|
|
96
76
|
}
|
|
97
77
|
|
|
98
78
|
// source returns the outgoing message source.
|
|
99
|
-
get source()
|
|
100
|
-
return this.
|
|
79
|
+
get source() {
|
|
80
|
+
return this._muxer.source
|
|
101
81
|
}
|
|
102
82
|
|
|
103
83
|
// streams returns the set of all ongoing streams.
|
|
@@ -122,7 +102,7 @@ export class StreamConn
|
|
|
122
102
|
|
|
123
103
|
// openStream implements the client open stream function.
|
|
124
104
|
public async openStream(): Promise<PacketStream> {
|
|
125
|
-
const strm = await this.muxer.
|
|
105
|
+
const strm = await this.muxer.newStream()
|
|
126
106
|
return streamToPacketStream(strm)
|
|
127
107
|
}
|
|
128
108
|
|
package/srpc/index.ts
CHANGED
package/srpc/log.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ComponentLogger, Logger } from '@libp2p/interface'
|
|
2
|
+
|
|
3
|
+
// https://github.com/libp2p/js-libp2p/issues/2276
|
|
4
|
+
// https://github.com/libp2p/js-libp2p/blob/bca8d6e689b47d85dda74082ed72e671139391de/packages/logger/src/index.ts#L86
|
|
5
|
+
// https://github.com/libp2p/js-libp2p/issues/2275
|
|
6
|
+
// https://github.com/ChainSafe/js-libp2p-yamux/issues/69
|
|
7
|
+
export function createDisabledLogger(namespace: string): Logger {
|
|
8
|
+
const logger = (): void => {}
|
|
9
|
+
logger.enabled = false
|
|
10
|
+
logger.color = ''
|
|
11
|
+
logger.diff = 0
|
|
12
|
+
logger.log = (): void => {}
|
|
13
|
+
logger.namespace = namespace
|
|
14
|
+
logger.destroy = () => true
|
|
15
|
+
logger.extend = () => logger
|
|
16
|
+
logger.debug = logger
|
|
17
|
+
logger.error = logger
|
|
18
|
+
logger.trace = logger
|
|
19
|
+
logger.newScope = () => logger
|
|
20
|
+
|
|
21
|
+
return logger
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function createDisabledComponentLogger(): ComponentLogger {
|
|
25
|
+
return { forComponent: createDisabledLogger }
|
|
26
|
+
}
|
package/srpc/stream.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Duplex, Source } from 'it-stream-types'
|
|
2
2
|
import { pipe } from 'it-pipe'
|
|
3
|
-
import
|
|
3
|
+
import { Stream } from '@libp2p/interface'
|
|
4
4
|
|
|
5
5
|
import type { Packet } from './rpcproto.pb.js'
|
|
6
6
|
import { combineUint8ArrayListTransform } from './array-list.js'
|
|
@@ -38,16 +38,9 @@ export function streamToPacketStream(stream: Stream): PacketStream {
|
|
|
38
38
|
combineUint8ArrayListTransform(),
|
|
39
39
|
),
|
|
40
40
|
sink: async (source: Source<Uint8Array>): Promise<void> => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
await stream.close()
|
|
46
|
-
} catch (err: unknown) {
|
|
47
|
-
await stream
|
|
48
|
-
.close({ signal: AbortSignal.timeout(1000) })
|
|
49
|
-
.catch(() => {})
|
|
50
|
-
}
|
|
41
|
+
await pipe(source, prependLengthPrefixTransform(), stream)
|
|
42
|
+
.catch((err) => stream.close(err))
|
|
43
|
+
.then(() => stream.close())
|
|
51
44
|
},
|
|
52
45
|
}
|
|
53
46
|
}
|
package/srpc/websocket.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { pipe } from 'it-pipe'
|
|
2
|
-
import
|
|
2
|
+
import { Direction } from '@libp2p/interface'
|
|
3
3
|
|
|
4
4
|
import duplex from '@aptre/it-ws/duplex'
|
|
5
5
|
import type WebSocket from '@aptre/it-ws/web-socket'
|
|
@@ -13,11 +13,7 @@ export class WebSocketConn extends StreamConn {
|
|
|
13
13
|
// socket is the web socket
|
|
14
14
|
private socket: WebSocket
|
|
15
15
|
|
|
16
|
-
constructor(
|
|
17
|
-
socket: WebSocket,
|
|
18
|
-
direction: MessageStreamDirection,
|
|
19
|
-
server?: Server,
|
|
20
|
-
) {
|
|
16
|
+
constructor(socket: WebSocket, direction: Direction, server?: Server) {
|
|
21
17
|
super(server, { direction })
|
|
22
18
|
this.socket = socket
|
|
23
19
|
const socketDuplex = duplex(socket)
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import type { AbortOptions, MessageStreamDirection } from '@libp2p/interface';
|
|
2
|
-
import { AbstractMessageStream, type SendResult } from '@libp2p/utils';
|
|
3
|
-
import type { Duplex, Source } from 'it-stream-types';
|
|
4
|
-
import { Uint8ArrayList } from 'uint8arraylist';
|
|
5
|
-
export interface DuplexMessageStreamInit {
|
|
6
|
-
direction?: MessageStreamDirection;
|
|
7
|
-
loggerName?: string;
|
|
8
|
-
inactivityTimeout?: number;
|
|
9
|
-
maxReadBufferLength?: number;
|
|
10
|
-
}
|
|
11
|
-
export declare class DuplexMessageStream extends AbstractMessageStream {
|
|
12
|
-
private readonly _outgoing;
|
|
13
|
-
constructor(init?: DuplexMessageStreamInit);
|
|
14
|
-
get source(): AsyncIterable<Uint8Array | Uint8ArrayList>;
|
|
15
|
-
get sink(): (source: Source<Uint8Array | Uint8ArrayList>) => Promise<void>;
|
|
16
|
-
sendData(data: Uint8ArrayList): SendResult;
|
|
17
|
-
sendReset(_err: Error): void;
|
|
18
|
-
sendPause(): void;
|
|
19
|
-
sendResume(): void;
|
|
20
|
-
close(_options?: AbortOptions): Promise<void>;
|
|
21
|
-
}
|
|
22
|
-
export declare function createDuplexMessageStream(init?: DuplexMessageStreamInit): DuplexMessageStream & Duplex<AsyncIterable<Uint8Array | Uint8ArrayList>>;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { logger } from '@libp2p/logger';
|
|
2
|
-
import { AbstractMessageStream, } from '@libp2p/utils';
|
|
3
|
-
import { pushable } from 'it-pushable';
|
|
4
|
-
// DuplexMessageStream wraps a Duplex stream as a MessageStream.
|
|
5
|
-
// This allows using duplex streams with the new libp2p StreamMuxer API.
|
|
6
|
-
//
|
|
7
|
-
// Extends AbstractMessageStream to get proper read/write buffer management,
|
|
8
|
-
// backpressure handling, and event semantics from libp2p.
|
|
9
|
-
export class DuplexMessageStream extends AbstractMessageStream {
|
|
10
|
-
// _outgoing is a pushable that collects data to be sent out.
|
|
11
|
-
_outgoing;
|
|
12
|
-
constructor(init) {
|
|
13
|
-
// Create the MessageStreamInit required by AbstractMessageStream
|
|
14
|
-
const streamInit = {
|
|
15
|
-
log: logger(init?.loggerName ?? 'starpc:duplex-message-stream'),
|
|
16
|
-
direction: init?.direction ?? 'outbound',
|
|
17
|
-
inactivityTimeout: init?.inactivityTimeout,
|
|
18
|
-
maxReadBufferLength: init?.maxReadBufferLength,
|
|
19
|
-
};
|
|
20
|
-
super(streamInit);
|
|
21
|
-
this._outgoing = pushable();
|
|
22
|
-
}
|
|
23
|
-
// source returns an async iterable that yields data to be sent to the remote.
|
|
24
|
-
get source() {
|
|
25
|
-
return this._outgoing;
|
|
26
|
-
}
|
|
27
|
-
// sink consumes data from the remote and feeds it into the stream.
|
|
28
|
-
// This is the receiving end of the duplex.
|
|
29
|
-
get sink() {
|
|
30
|
-
return async (source) => {
|
|
31
|
-
try {
|
|
32
|
-
for await (const data of source) {
|
|
33
|
-
if (this.status === 'closed' ||
|
|
34
|
-
this.status === 'aborted' ||
|
|
35
|
-
this.status === 'reset') {
|
|
36
|
-
break;
|
|
37
|
-
}
|
|
38
|
-
// Use the parent's onData method which handles buffering and events
|
|
39
|
-
this.onData(data);
|
|
40
|
-
}
|
|
41
|
-
// Remote closed their write side
|
|
42
|
-
this.onRemoteCloseWrite();
|
|
43
|
-
}
|
|
44
|
-
catch (err) {
|
|
45
|
-
this.abort(err);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
// sendData implements AbstractMessageStream.sendData
|
|
50
|
-
// Called by the parent class when processing the write queue.
|
|
51
|
-
sendData(data) {
|
|
52
|
-
// Push data to the outgoing pushable
|
|
53
|
-
this._outgoing.push(data);
|
|
54
|
-
return {
|
|
55
|
-
sentBytes: data.byteLength,
|
|
56
|
-
canSendMore: true, // Our pushable can always accept more
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
// sendReset implements AbstractMessageStream.sendReset
|
|
60
|
-
// Called when the stream is aborted locally.
|
|
61
|
-
sendReset(_err) {
|
|
62
|
-
// End the outgoing pushable - we can't send a reset over a generic duplex
|
|
63
|
-
this._outgoing.end();
|
|
64
|
-
}
|
|
65
|
-
// sendPause implements AbstractMessageStream.sendPause
|
|
66
|
-
// Called when the stream is paused.
|
|
67
|
-
sendPause() {
|
|
68
|
-
// No-op: generic duplex streams don't support pause signaling
|
|
69
|
-
this.log.trace('pause requested (no-op for duplex stream)');
|
|
70
|
-
}
|
|
71
|
-
// sendResume implements AbstractMessageStream.sendResume
|
|
72
|
-
// Called when the stream is resumed.
|
|
73
|
-
sendResume() {
|
|
74
|
-
// No-op: generic duplex streams don't support resume signaling
|
|
75
|
-
this.log.trace('resume requested (no-op for duplex stream)');
|
|
76
|
-
}
|
|
77
|
-
// close gracefully closes the stream.
|
|
78
|
-
async close(_options) {
|
|
79
|
-
if (this.status === 'closed' || this.status === 'closing') {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
this.status = 'closing';
|
|
83
|
-
this.writeStatus = 'closing';
|
|
84
|
-
// End the outgoing pushable to signal we're done writing
|
|
85
|
-
this._outgoing.end();
|
|
86
|
-
this.writeStatus = 'closed';
|
|
87
|
-
if (this.readStatus === 'closed') {
|
|
88
|
-
this.onTransportClosed();
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
// createDuplexMessageStream creates a new DuplexMessageStream.
|
|
93
|
-
export function createDuplexMessageStream(init) {
|
|
94
|
-
return new DuplexMessageStream(init);
|
|
95
|
-
}
|
|
@@ -1,132 +0,0 @@
|
|
|
1
|
-
import type { AbortOptions, MessageStreamDirection } from '@libp2p/interface'
|
|
2
|
-
import { logger } from '@libp2p/logger'
|
|
3
|
-
import {
|
|
4
|
-
AbstractMessageStream,
|
|
5
|
-
type MessageStreamInit,
|
|
6
|
-
type SendResult,
|
|
7
|
-
} from '@libp2p/utils'
|
|
8
|
-
import type { Duplex, Source } from 'it-stream-types'
|
|
9
|
-
import { pushable, type Pushable } from 'it-pushable'
|
|
10
|
-
import { Uint8ArrayList } from 'uint8arraylist'
|
|
11
|
-
|
|
12
|
-
// DuplexMessageStreamInit are parameters for DuplexMessageStream.
|
|
13
|
-
export interface DuplexMessageStreamInit {
|
|
14
|
-
// direction is the stream direction.
|
|
15
|
-
direction?: MessageStreamDirection
|
|
16
|
-
// loggerName is the name to use for the logger.
|
|
17
|
-
loggerName?: string
|
|
18
|
-
// inactivityTimeout is the inactivity timeout in ms.
|
|
19
|
-
inactivityTimeout?: number
|
|
20
|
-
// maxReadBufferLength is the max read buffer length.
|
|
21
|
-
maxReadBufferLength?: number
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// DuplexMessageStream wraps a Duplex stream as a MessageStream.
|
|
25
|
-
// This allows using duplex streams with the new libp2p StreamMuxer API.
|
|
26
|
-
//
|
|
27
|
-
// Extends AbstractMessageStream to get proper read/write buffer management,
|
|
28
|
-
// backpressure handling, and event semantics from libp2p.
|
|
29
|
-
export class DuplexMessageStream extends AbstractMessageStream {
|
|
30
|
-
// _outgoing is a pushable that collects data to be sent out.
|
|
31
|
-
private readonly _outgoing: Pushable<Uint8Array | Uint8ArrayList>
|
|
32
|
-
|
|
33
|
-
constructor(init?: DuplexMessageStreamInit) {
|
|
34
|
-
// Create the MessageStreamInit required by AbstractMessageStream
|
|
35
|
-
const streamInit: MessageStreamInit = {
|
|
36
|
-
log: logger(init?.loggerName ?? 'starpc:duplex-message-stream'),
|
|
37
|
-
direction: init?.direction ?? 'outbound',
|
|
38
|
-
inactivityTimeout: init?.inactivityTimeout,
|
|
39
|
-
maxReadBufferLength: init?.maxReadBufferLength,
|
|
40
|
-
}
|
|
41
|
-
super(streamInit)
|
|
42
|
-
this._outgoing = pushable<Uint8Array | Uint8ArrayList>()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// source returns an async iterable that yields data to be sent to the remote.
|
|
46
|
-
get source(): AsyncIterable<Uint8Array | Uint8ArrayList> {
|
|
47
|
-
return this._outgoing
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// sink consumes data from the remote and feeds it into the stream.
|
|
51
|
-
// This is the receiving end of the duplex.
|
|
52
|
-
get sink(): (source: Source<Uint8Array | Uint8ArrayList>) => Promise<void> {
|
|
53
|
-
return async (
|
|
54
|
-
source: Source<Uint8Array | Uint8ArrayList>,
|
|
55
|
-
): Promise<void> => {
|
|
56
|
-
try {
|
|
57
|
-
for await (const data of source) {
|
|
58
|
-
if (
|
|
59
|
-
this.status === 'closed' ||
|
|
60
|
-
this.status === 'aborted' ||
|
|
61
|
-
this.status === 'reset'
|
|
62
|
-
) {
|
|
63
|
-
break
|
|
64
|
-
}
|
|
65
|
-
// Use the parent's onData method which handles buffering and events
|
|
66
|
-
this.onData(data)
|
|
67
|
-
}
|
|
68
|
-
// Remote closed their write side
|
|
69
|
-
this.onRemoteCloseWrite()
|
|
70
|
-
} catch (err) {
|
|
71
|
-
this.abort(err as Error)
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// sendData implements AbstractMessageStream.sendData
|
|
77
|
-
// Called by the parent class when processing the write queue.
|
|
78
|
-
sendData(data: Uint8ArrayList): SendResult {
|
|
79
|
-
// Push data to the outgoing pushable
|
|
80
|
-
this._outgoing.push(data)
|
|
81
|
-
return {
|
|
82
|
-
sentBytes: data.byteLength,
|
|
83
|
-
canSendMore: true, // Our pushable can always accept more
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// sendReset implements AbstractMessageStream.sendReset
|
|
88
|
-
// Called when the stream is aborted locally.
|
|
89
|
-
sendReset(_err: Error): void {
|
|
90
|
-
// End the outgoing pushable - we can't send a reset over a generic duplex
|
|
91
|
-
this._outgoing.end()
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// sendPause implements AbstractMessageStream.sendPause
|
|
95
|
-
// Called when the stream is paused.
|
|
96
|
-
sendPause(): void {
|
|
97
|
-
// No-op: generic duplex streams don't support pause signaling
|
|
98
|
-
this.log.trace('pause requested (no-op for duplex stream)')
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// sendResume implements AbstractMessageStream.sendResume
|
|
102
|
-
// Called when the stream is resumed.
|
|
103
|
-
sendResume(): void {
|
|
104
|
-
// No-op: generic duplex streams don't support resume signaling
|
|
105
|
-
this.log.trace('resume requested (no-op for duplex stream)')
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// close gracefully closes the stream.
|
|
109
|
-
async close(_options?: AbortOptions): Promise<void> {
|
|
110
|
-
if (this.status === 'closed' || this.status === 'closing') {
|
|
111
|
-
return
|
|
112
|
-
}
|
|
113
|
-
this.status = 'closing'
|
|
114
|
-
this.writeStatus = 'closing'
|
|
115
|
-
|
|
116
|
-
// End the outgoing pushable to signal we're done writing
|
|
117
|
-
this._outgoing.end()
|
|
118
|
-
|
|
119
|
-
this.writeStatus = 'closed'
|
|
120
|
-
if (this.readStatus === 'closed') {
|
|
121
|
-
this.onTransportClosed()
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// createDuplexMessageStream creates a new DuplexMessageStream.
|
|
127
|
-
export function createDuplexMessageStream(
|
|
128
|
-
init?: DuplexMessageStreamInit,
|
|
129
|
-
): DuplexMessageStream & Duplex<AsyncIterable<Uint8Array | Uint8ArrayList>> {
|
|
130
|
-
return new DuplexMessageStream(init) as DuplexMessageStream &
|
|
131
|
-
Duplex<AsyncIterable<Uint8Array | Uint8ArrayList>>
|
|
132
|
-
}
|