bare-rpc 0.2.6 → 0.2.7

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 CHANGED
@@ -11,6 +11,7 @@ npm i bare-rpc
11
11
  ```js
12
12
  import RPC from 'bare-rpc'
13
13
 
14
+ // On one end
14
15
  const rpc = new RPC(stream, (req) => {
15
16
  if (req.command === 42) {
16
17
  console.log(req.data.toString()) // ping
@@ -30,7 +31,7 @@ console.log(replyBuffer.toString()) // pong
30
31
 
31
32
  ### `RPC`
32
33
 
33
- #### `const rpc = new RPC(stream, [onrequest])`
34
+ #### `const rpc = new RPC(stream[, onrequest])`
34
35
 
35
36
  Create an RPC instance using a duplex `stream`. `onrequest` is an optional callback run when a remote request is received. This is where processing and responding to an RPC request happens. `onrequest` receives a `RPCIncomingRequest` as an argument, for example:
36
37
 
@@ -64,7 +65,7 @@ A boolean for whether the request has been sent.
64
65
 
65
66
  A boolean for whether the request has received a reply.
66
67
 
67
- #### `req.send(data, [encoding])`
68
+ #### `req.send(data[, encoding])`
68
69
 
69
70
  Send the request with the provided `data`. `data` can be a buffer or a string which will be encoded using `encoding`.
70
71
 
@@ -74,13 +75,13 @@ Send the request with the provided `data`. `data` can be a buffer or a string wh
74
75
 
75
76
  Await the reply from the remote end to the request. `encoding` can be defined for decoding the response `data` buffer back into a string.
76
77
 
77
- #### `const stream = req.createRequestStream([opts])`
78
+ #### `const stream = req.createRequestStream([options])`
78
79
 
79
- Create a [`Writable`](https://github.com/mafintosh/streamx?tab=readme-ov-file#writable-stream) stream for sending data with the request.
80
+ Create a [`Writable`](https://github.com/mafintosh/streamx#writable-stream) stream for sending data with the request.
80
81
 
81
- #### `const stream = req.createResponseStream([opts])`
82
+ #### `const stream = req.createResponseStream([options])`
82
83
 
83
- Create a [`Readable`](https://github.com/mafintosh/streamx?tab=readme-ov-file#readable-stream) stream for receiving data in reply to the request.
84
+ Create a [`Readable`](https://github.com/mafintosh/streamx#readable-stream) stream for receiving data in reply to the request.
84
85
 
85
86
  ### `RPCIncomingRequest`
86
87
 
@@ -104,11 +105,11 @@ A boolean for whether the request has been received as a stream. See [`req.creat
104
105
 
105
106
  Reply to the request with the provided `data`. `data` can be a buffer or a string which will be encoded using `encoding`.
106
107
 
107
- #### `const stream = req.createRequestStream([opts])`
108
+ #### `const stream = req.createRequestStream([options])`
108
109
 
109
110
  Create a `Readable` stream for receiving data from the request.
110
111
 
111
- #### `const stream = req.createResponseStream([opts])`
112
+ #### `const stream = req.createResponseStream([options])`
112
113
 
113
114
  Create a `Writable` stream for sending data in reply to the request.
114
115
 
@@ -118,8 +119,7 @@ An alternative way to define commands and handlers for receiving them.
118
119
 
119
120
  #### `const router = new RPC.CommandRouter()`
120
121
 
121
- Create a new command router. This router can then be used when creating an
122
- `rpc`. For example:
122
+ Create a new command router. This router can then be used when creating an `rpc`. For example:
123
123
 
124
124
  ```js
125
125
  const router = new RPC.CommandRouter()
@@ -135,16 +135,18 @@ const req = rpc.request(42)
135
135
  req.send('ping')
136
136
  ```
137
137
 
138
- #### `router.respond(command, [opts], async (req, data) => {})`
138
+ #### `router.respond(command[, options], async (req, data) => {})`
139
139
 
140
140
  Define a command and the handler for it. The callback for a command receives both the request (`req`) and the `data` buffer and can return a value to respond. If the request is responded to in the callback, the return value is ignored.
141
141
 
142
- `opts` can include:
142
+ Options include:
143
143
 
144
144
  ```
145
145
  {
146
- requestEncoding: c.raw, // Encoding for decoding incoming request
147
- responseEncoding: c.raw, // Encoding for outgoing response
146
+ // Encoding for incoming request
147
+ requestEncoding: c.raw,
148
+ // Encoding for outgoing response
149
+ responseEncoding: c.raw,
148
150
  }
149
151
  ```
150
152
 
package/index.js CHANGED
@@ -12,7 +12,6 @@ const CommandRouter = require('./lib/command-router')
12
12
  module.exports = exports = class RPC {
13
13
  constructor(stream, onrequest = noop) {
14
14
  this._stream = stream
15
-
16
15
  this._id = 0
17
16
 
18
17
  this._outgoingRequests = new Map()
@@ -43,7 +42,11 @@ module.exports = exports = class RPC {
43
42
  }
44
43
 
45
44
  _sendMessage(message) {
46
- this._stream.write(c.encode(m.message, message))
45
+ const header = c.encode(m.header, message)
46
+
47
+ this._stream.write(header)
48
+
49
+ if (message.data) this._stream.write(message.data)
47
50
  }
48
51
 
49
52
  _sendRequest(request, data = null) {
@@ -1,4 +1,4 @@
1
- const b4a = require('b4a')
1
+ const c = require('compact-encoding')
2
2
  const errors = require('./errors')
3
3
 
4
4
  module.exports = class RPCIncomingRequest {
@@ -19,12 +19,16 @@ module.exports = class RPCIncomingRequest {
19
19
  throw errors.ALREADY_SENT('Response has already been sent')
20
20
  }
21
21
 
22
+ encoding =
23
+ encoding && encoding !== 'buffer'
24
+ ? c.from(encoding)
25
+ : typeof data === 'string'
26
+ ? c.raw.utf8
27
+ : null
28
+
22
29
  this.sent = true
23
30
 
24
- this.rpc._sendResponse(
25
- this,
26
- typeof data === 'string' ? b4a.from(data, encoding) : data
27
- )
31
+ this.rpc._sendResponse(this, encoding ? c.encode(encoding, data) : data)
28
32
  }
29
33
 
30
34
  createResponseStream(opts = {}) {
package/lib/messages.js CHANGED
@@ -2,26 +2,6 @@ const c = require('compact-encoding')
2
2
  const { type: t, stream: s } = require('./constants')
3
3
  const errors = require('./errors')
4
4
 
5
- const request = {
6
- preencode(state, m) {
7
- c.uint.preencode(state, m.command)
8
- c.uint.preencode(state, m.stream)
9
- if (m.stream === 0) c.buffer.preencode(state, m.data)
10
- },
11
- encode(state, m) {
12
- c.uint.encode(state, m.command)
13
- c.uint.encode(state, m.stream)
14
- if (m.stream === 0) c.buffer.encode(state, m.data)
15
- },
16
- decode(state, id) {
17
- const command = c.uint.decode(state)
18
- const stream = c.uint.decode(state)
19
- const data = stream === 0 ? c.buffer.decode(state) : null
20
-
21
- return { type: t.REQUEST, id, command, stream, data }
22
- }
23
- }
24
-
25
5
  const error = {
26
6
  preencode(state, m) {
27
7
  c.utf8.preencode(state, m.message)
@@ -41,79 +21,38 @@ const error = {
41
21
  }
42
22
  }
43
23
 
44
- const response = {
45
- preencode(state, m) {
46
- c.bool.preencode(state, !!m.error)
47
- c.uint.preencode(state, m.stream)
48
-
49
- if (m.error) error.preencode(state, m.error)
50
- else if (m.stream === 0) c.buffer.preencode(state, m.data)
51
- },
52
- encode(state, m) {
53
- c.bool.encode(state, !!m.error)
54
- c.uint.encode(state, m.stream)
55
-
56
- if (m.error) error.encode(state, m.error)
57
- else if (m.stream === 0) c.buffer.encode(state, m.data)
58
- },
59
- decode(state) {
60
- const err = c.bool.decode(state)
61
- const stream = c.uint.decode(state)
62
-
63
- if (err) {
64
- return { stream, error: error.decode(state), data: null }
65
- }
66
-
67
- if (stream === 0) {
68
- return { stream, error: null, data: c.buffer.decode(state) }
69
- }
70
-
71
- return { stream, error: null, data: null }
72
- }
73
- }
74
-
75
- const stream = {
76
- preencode(state, m) {
77
- c.uint.preencode(state, m.stream)
78
-
79
- if (m.stream & s.ERROR) error.preencode(state, m.error)
80
- else if (m.stream & s.DATA) c.buffer.preencode(state, m.data)
81
- },
82
- encode(state, m) {
83
- c.uint.encode(state, m.stream)
84
-
85
- if (m.stream & s.ERROR) error.encode(state, m.error)
86
- else if (m.stream & s.DATA) c.buffer.encode(state, m.data)
87
- },
88
- decode(state) {
89
- const stream = c.uint.decode(state)
90
-
91
- if (stream & s.ERROR)
92
- return { stream, error: error.decode(state), data: null }
93
- if (stream & s.DATA)
94
- return { stream, error: null, data: c.buffer.decode(state) }
95
-
96
- return { stream, error: null, data: null }
97
- }
98
- }
99
-
100
- exports.message = {
24
+ exports.header = {
101
25
  preencode(state, m) {
102
26
  c.uint32.preencode(state, 0) // Frame
103
27
  c.uint.preencode(state, m.type)
104
28
  c.uint.preencode(state, m.id)
105
29
 
30
+ let hasData = false
31
+
106
32
  switch (m.type) {
107
33
  case t.REQUEST:
108
- request.preencode(state, m)
34
+ c.uint.preencode(state, m.command)
35
+ c.uint.preencode(state, m.stream)
36
+ if (m.stream === 0) hasData = true
109
37
  break
38
+
110
39
  case t.RESPONSE:
111
- response.preencode(state, m)
40
+ c.bool.preencode(state, !!m.error)
41
+ c.uint.preencode(state, m.stream)
42
+
43
+ if (m.error) error.preencode(state, m.error)
44
+ else if (m.stream === 0) hasData = true
112
45
  break
46
+
113
47
  case t.STREAM:
114
- stream.preencode(state, m)
48
+ c.uint.preencode(state, m.stream)
49
+
50
+ if (m.stream & s.ERROR) error.preencode(state, m.error)
51
+ else if (m.stream & s.DATA) hasData = true
115
52
  break
116
53
  }
54
+
55
+ if (hasData) c.uint.preencode(state, m.data.byteLength)
117
56
  },
118
57
  encode(state, m) {
119
58
  const frame = state.start
@@ -125,26 +64,44 @@ exports.message = {
125
64
  c.uint.encode(state, m.type)
126
65
  c.uint.encode(state, m.id)
127
66
 
67
+ let hasData = false
68
+
128
69
  switch (m.type) {
129
70
  case t.REQUEST:
130
- request.encode(state, m)
71
+ c.uint.encode(state, m.command)
72
+ c.uint.encode(state, m.stream)
73
+ if (m.stream === 0) hasData = true
131
74
  break
75
+
132
76
  case t.RESPONSE:
133
- response.encode(state, m)
77
+ c.bool.encode(state, !!m.error)
78
+ c.uint.encode(state, m.stream)
79
+
80
+ if (m.error) error.encode(state, m.error)
81
+ else if (m.stream === 0) hasData = true
134
82
  break
83
+
135
84
  case t.STREAM:
136
- stream.encode(state, m)
85
+ c.uint.encode(state, m.stream)
86
+
87
+ if (m.stream & s.ERROR) error.encode(state, m.error)
88
+ else if (m.stream & s.DATA) hasData = true
137
89
  break
138
90
  }
139
91
 
92
+ if (hasData) c.uint.encode(state, m.data.byteLength)
93
+
140
94
  const end = state.start
141
95
 
142
96
  state.start = frame
143
97
 
144
- c.uint32.encode(state, end - start)
98
+ c.uint32.encode(state, end - start + (hasData ? m.data.byteLength : 0))
145
99
 
146
100
  state.start = end
147
- },
101
+ }
102
+ }
103
+
104
+ exports.message = {
148
105
  decode(state) {
149
106
  const frame = c.uint32.decode(state)
150
107
 
@@ -154,12 +111,42 @@ exports.message = {
154
111
  const id = c.uint.decode(state)
155
112
 
156
113
  switch (type) {
157
- case t.REQUEST:
158
- return { type, id, ...request.decode(state, id) }
159
- case t.RESPONSE:
160
- return { type, id, ...response.decode(state, id) }
114
+ case t.REQUEST: {
115
+ const command = c.uint.decode(state)
116
+ const stream = c.uint.decode(state)
117
+ const data = stream === 0 ? c.buffer.decode(state) : null
118
+
119
+ return { type, id, command, stream, data }
120
+ }
121
+
122
+ case t.RESPONSE: {
123
+ const err = c.bool.decode(state)
124
+ const stream = c.uint.decode(state)
125
+
126
+ if (err) {
127
+ return { type, id, stream, error: error.decode(state), data: null }
128
+ }
129
+
130
+ if (stream === 0) {
131
+ return { type, id, stream, error: null, data: c.buffer.decode(state) }
132
+ }
133
+
134
+ return { type, id, stream, error: null, data: null }
135
+ }
136
+
161
137
  case t.STREAM:
162
- return { type, id, ...stream.decode(state, id) }
138
+ const stream = c.uint.decode(state)
139
+
140
+ if (stream & s.ERROR) {
141
+ return { type, id, stream, error: error.decode(state), data: null }
142
+ }
143
+
144
+ if (stream & s.DATA) {
145
+ return { type, id, stream, error: null, data: c.buffer.decode(state) }
146
+ }
147
+
148
+ return { type, id, stream, error: null, data: null }
149
+
163
150
  default:
164
151
  throw errors.UNKNOWN_MESSAGE(`Unknown message '${type}'`)
165
152
  }
@@ -1,4 +1,4 @@
1
- const b4a = require('b4a')
1
+ const c = require('compact-encoding')
2
2
  const errors = require('./errors')
3
3
 
4
4
  module.exports = class RPCOutgoingRequest {
@@ -23,12 +23,16 @@ module.exports = class RPCOutgoingRequest {
23
23
  throw errors.ALREADY_SENT('Request has already been sent')
24
24
  }
25
25
 
26
+ encoding =
27
+ encoding && encoding !== 'buffer'
28
+ ? c.from(encoding)
29
+ : typeof data === 'string'
30
+ ? c.raw.utf8
31
+ : null
32
+
26
33
  this.sent = true
27
34
 
28
- this.rpc._sendRequest(
29
- this,
30
- typeof data === 'string' ? b4a.from(data, encoding) : data
31
- )
35
+ this.rpc._sendRequest(this, encoding ? c.encode(encoding, data) : data)
32
36
  }
33
37
 
34
38
  reply(encoding) {
@@ -36,10 +40,12 @@ module.exports = class RPCOutgoingRequest {
36
40
  throw errors.ALREADY_RECEIVED('Response is already being received')
37
41
  }
38
42
 
43
+ encoding = encoding && encoding !== 'buffer' ? c.from(encoding) : null
44
+
39
45
  this.received = true
40
46
 
41
- return encoding && encoding !== 'buffer'
42
- ? this._promise.then((data) => b4a.toString(data, encoding))
47
+ return encoding
48
+ ? this._promise.then((data) => c.decode(encoding, data))
43
49
  : this._promise
44
50
  }
45
51
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bare-rpc",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "librpc ABI compatible RPC for Bare",
5
5
  "exports": {
6
6
  ".": {