@weave-js/core 0.9.2 → 0.11.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 (98) hide show
  1. package/lib/ExtendableError.js +33 -0
  2. package/lib/broker/context.js +25 -13
  3. package/lib/broker/{default-options.js → defaultOptions.js} +23 -5
  4. package/lib/broker/index.js +224 -194
  5. package/lib/buildRuntime.js +70 -0
  6. package/lib/cache/base.js +47 -31
  7. package/lib/cache/inMemory.js +140 -0
  8. package/lib/cache/index.js +2 -2
  9. package/lib/cache/lock.js +21 -4
  10. package/lib/constants.js +8 -5
  11. package/lib/errorHandler.js +19 -0
  12. package/lib/errors.js +17 -10
  13. package/lib/helper/{define-action.js → defineAction.js} +3 -1
  14. package/lib/helper/{define-broker-options.js → defineBrokerOptions.js} +2 -0
  15. package/lib/helper/{define-service.js → defineService.js} +4 -1
  16. package/lib/index.js +15 -89
  17. package/lib/logger/base.js +3 -3
  18. package/lib/logger/format/asHumanReadable.js +42 -0
  19. package/lib/logger/format/asJson.js +25 -0
  20. package/lib/logger/format/index.js +4 -0
  21. package/lib/logger/index.js +12 -5
  22. package/lib/logger/levels.js +9 -8
  23. package/lib/logger/tools.js +4 -25
  24. package/lib/logger/utils/colorize.js +32 -0
  25. package/lib/logger/utils/format.js +18 -4
  26. package/lib/metrics/constants.js +8 -6
  27. package/lib/metrics/types/gauge.js +1 -1
  28. package/lib/middlewares/action-hooks/index.js +62 -14
  29. package/lib/middlewares/bulkhead/index.js +5 -4
  30. package/lib/middlewares/cache/index.js +80 -0
  31. package/lib/middlewares/circuit-breaker/index.js +41 -16
  32. package/lib/middlewares/context-tracker/index.js +3 -3
  33. package/lib/middlewares/error-handler/index.js +3 -3
  34. package/lib/middlewares/index.js +2 -1
  35. package/lib/middlewares/metrics/getMiddlewareWrapper.js +2 -2
  36. package/lib/middlewares/metrics/index.js +2 -2
  37. package/lib/middlewares/retry/index.js +11 -10
  38. package/lib/middlewares/timeout/index.js +5 -5
  39. package/lib/middlewares/tracing/index.js +27 -14
  40. package/lib/middlewares/tracing/tags.js +43 -0
  41. package/lib/middlewares/validator/index.js +6 -6
  42. package/lib/registry/{action-endpoint.js → actionEndpoint.js} +0 -0
  43. package/lib/registry/collections/{action-collection.js → actionCollection.js} +2 -2
  44. package/lib/registry/collections/{endpoint-collection.js → endpointCollection.js} +19 -3
  45. package/lib/registry/collections/{event-collection.js → eventCollection.js} +15 -9
  46. package/lib/registry/collections/{node-collection.js → nodeCollection.js} +1 -1
  47. package/lib/registry/collections/{service-collection.js → serviceCollection.js} +2 -2
  48. package/lib/registry/{event-endpoint.js → eventEndpoint.js} +1 -1
  49. package/lib/registry/node.js +1 -1
  50. package/lib/registry/registry.js +10 -23
  51. package/lib/registry/service/service.js +2 -2
  52. package/lib/registry/{service-item.js → serviceItem.js} +1 -1
  53. package/lib/runtime/{init-action-invoker.js → initActionInvoker.js} +3 -3
  54. package/lib/runtime/{init-cache.js → initCache.js} +0 -0
  55. package/lib/runtime/{init-context-factory.js → initContextFactory.js} +2 -2
  56. package/lib/runtime/{init-eventbus.js → initEventbus.js} +11 -10
  57. package/lib/runtime/{init-logger.js → initLogger.js} +0 -0
  58. package/lib/runtime/{init-metrics.js → initMetrics.js} +11 -10
  59. package/lib/runtime/{init-middleware-manager.js → initMiddlewareManager.js} +1 -1
  60. package/lib/runtime/{init-registry.js → initRegistry.js} +1 -1
  61. package/lib/runtime/{init-service-manager.js → initServiceManager.js} +0 -0
  62. package/lib/runtime/{init-tracing.js → initTracing.js} +2 -1
  63. package/lib/runtime/{init-transport.js → initTransport.js} +1 -1
  64. package/lib/runtime/{init-uuid-factory.js → initUuidFactory.js} +0 -0
  65. package/lib/runtime/{init-validator.js → initValidator.js} +1 -1
  66. package/lib/tracing/collectors/base.js +11 -19
  67. package/lib/tracing/collectors/event.js +2 -2
  68. package/lib/tracing/collectors/index.js +2 -2
  69. package/lib/tracing/span.js +8 -6
  70. package/lib/transport/InboundTransformStream.js +75 -0
  71. package/lib/transport/adapters/{adapter-base.js → adapteBase.js} +16 -1
  72. package/lib/transport/adapters/adapters.js +1 -1
  73. package/lib/transport/adapters/dummy/index.js +3 -3
  74. package/lib/transport/adapters/index.js +1 -1
  75. package/lib/transport/adapters/tcp/discovery/index.js +45 -31
  76. package/lib/transport/adapters/tcp/index.js +20 -13
  77. package/lib/transport/adapters/tcp/tcpReader.js +1 -0
  78. package/lib/transport/adapters/tcp/tcpWriteStream.js +1 -1
  79. package/lib/transport/adapters/tcp/tcpWriter.js +2 -2
  80. package/lib/transport/createMessage.js +11 -0
  81. package/lib/transport/{transport-factory.js → createTransport.js} +234 -173
  82. package/lib/transport/errorPayloadFactory.js +18 -0
  83. package/lib/transport/{message-handlers.js → messageHandlers.js} +93 -24
  84. package/lib/transport/messageTypes.js +23 -0
  85. package/lib/types.js +66 -66
  86. package/lib/utils/index.js +0 -1
  87. package/lib/utils/options.js +4 -2
  88. package/lib/utils/{restore-error.js → restoreError.js} +1 -1
  89. package/package.json +6 -12
  90. package/CHANGELOG.md +0 -1293
  91. package/lib/cache/memory.js +0 -91
  92. package/lib/middlewares/tracing/buildTags.js +0 -17
  93. package/lib/new-errors.js +0 -25
  94. package/lib/tracing/collectors/console.js +0 -34
  95. package/lib/tracing/index.js +0 -3
  96. package/lib/tracing/tracer.js +0 -64
  97. package/lib/transport/message-types.js +0 -59
  98. package/lib/transport/message.js +0 -16
@@ -1,7 +1,7 @@
1
1
  /*
2
2
  * Author: Kevin Ries (kevin@fachw3rk.de)
3
3
  * -----
4
- * Copyright 2020 Fachwerk
4
+ * Copyright 2021 Fachwerk
5
5
  */
6
6
 
7
7
  /**
@@ -9,13 +9,16 @@
9
9
  * @typedef {import('../types.js').TransportAdapter} TransportAdapter
10
10
  * @typedef {import('../types.js').Transport} Transport
11
11
  * @typedef {import('../types').TransportMessage} TransportMessage
12
+ * @typedef {import('../types').Context} Context
12
13
  */
13
14
 
14
15
  // Own packages
15
16
  const { WeaveError, WeaveQueueSizeExceededError } = require('../errors')
16
- const MessageTypes = require('./message-types')
17
+ const { createMessage } = require('./createMessage')
18
+ const MessageTypes = require('./messageTypes')
17
19
  const utils = require('@weave-js/utils')
18
- const createMessageHandler = require('./message-handlers')
20
+ const createMessageHandler = require('./messageHandlers')
21
+ const { errorPayloadFactory } = require('./errorPayloadFactory')
19
22
 
20
23
  /**
21
24
  * Create a Transport adapter
@@ -25,139 +28,30 @@ const createMessageHandler = require('./message-handlers')
25
28
  */
26
29
  exports.createTransport = (runtime, adapter) => {
27
30
  const transport = Object.create(null)
28
- const { nodeId, middlewareHandler } = runtime
31
+ const { nodeId, middlewareHandler, createLogger } = runtime
29
32
 
30
33
  let heartbeatTimer
31
34
  let checkNodesTimer
32
35
  let checkOfflineNodesTimer
33
36
  let updateLocalNodeTimer
34
37
 
35
- const log = runtime.createLogger('TRANSPORT')
36
-
38
+ // Pending action store
37
39
  const pending = {
38
40
  requests: new Map(),
39
41
  requestStreams: new Map(),
40
- responseStreams: new Map()
42
+ responseStreams: new Map(),
43
+ outboundResponseStreams: new Map(),
44
+ outboundRequestStreams: new Map()
41
45
  }
42
46
 
43
- // Outgoing request
44
- const doRequest = (context, resolve, reject) => {
45
- const isStream = utils.isStream(context.options.stream)
46
-
47
- const request = {
48
- targetNodeId: context.nodeId,
49
- action: context.action.name,
50
- context,
51
- resolve,
52
- reject,
53
- isStream
54
- }
55
-
56
- log.debug(`Send request for ${request.action} to node ${request.targetNodeId}.`)
57
-
58
- pending.requests.set(context.id, request)
59
-
60
- const payload = {
61
- id: context.id,
62
- action: context.action.name,
63
- data: context.data,
64
- timeout: context.options.timeout,
65
- meta: context.meta,
66
- level: context.level,
67
- metrics: context.metrics,
68
- requestId: context.requestId,
69
- parentId: context.parentId,
70
- isStream
71
- }
72
-
73
- // Handle object mode streams
74
- if (isStream) {
75
- if (utils.isStreamObjectMode(context.options.stream)) {
76
- payload.meta = payload.meta || {}
77
- payload.meta.$isObjectModeStream = true
78
- }
79
- payload.sequence = 0
80
- }
81
-
82
- const message = transport.createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payload)
83
-
84
- return transport.send(message)
85
- .then(() => {
86
- // send stream
87
- if (isStream) {
88
- const stream = context.options.stream
89
- payload.meta = {}
90
-
91
- if (utils.isStreamObjectMode(context.options.stream)) {
92
- payload.meta.$isObjectModeStream = true
93
- }
94
-
95
- stream.on('data', data => {
96
- stream.pause()
97
- const chunks = []
98
-
99
- // The chunk is larger than maxBufferSize
100
- if (data instanceof Buffer && runtime.options.transport.maxChunkSize > 0 && data.length > runtime.options.transport.maxChunkSize) {
101
- const length = data.length
102
- let i = 0
103
- while (i < length) {
104
- chunks.push(data.slice(i, i += runtime.options.transport.maxChunkSize))
105
- }
106
- } else {
107
- chunks.push(data)
108
- }
109
-
110
- // Send chunks from chunk buffer
111
- for (const chunk of chunks) {
112
- const payloadCopy = Object.assign({}, payload)
113
- payloadCopy.sequence = ++payload.sequence
114
- payloadCopy.chunk = chunk
115
- payloadCopy.isStream = true
116
- payload.data = null
117
-
118
- const message = transport.createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payloadCopy)
119
- transport.send(message)
120
- }
121
-
122
- // resume stream
123
- stream.resume()
124
- return
125
- })
126
-
127
- stream.on('end', () => {
128
- const payloadCopy = Object.assign({}, payload)
129
- payloadCopy.sequence = ++payload.sequence
130
- payloadCopy.chunk = null
131
- payloadCopy.isStream = false
132
- const message = transport.createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payloadCopy)
133
- return transport.send(message)
134
- })
135
-
136
- stream.on('error', (_) => {
137
- const payloadCopy = Object.assign({}, payload)
138
- payloadCopy.sequence = ++payload.sequence
139
- payloadCopy.chunk = null
140
- payloadCopy.isStream = false
141
-
142
- // todo: Attach error on payload
143
-
144
- const message = transport.createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payloadCopy)
145
- return transport.send(message)
146
- })
147
- }
148
- })
149
- .catch((error) => {
150
- reject(error)
151
- })
152
- }
153
-
154
- transport.log = runtime.createLogger('TRANSPORT')
47
+ transport.log = createLogger('TRANSPORT')
48
+ transport.reconnectInProgress = false
155
49
  transport.isConnected = false
50
+ transport.isDisconnecting = false
156
51
  transport.isReady = false
157
52
  transport.pending = pending
158
53
  transport.resolveConnect = null
159
54
  transport.adapterName = adapter.name
160
-
161
55
  transport.statistics = {
162
56
  received: {
163
57
  packages: 0
@@ -167,15 +61,24 @@ exports.createTransport = (runtime, adapter) => {
167
61
  }
168
62
  }
169
63
 
64
+ const getErrorPayload = errorPayloadFactory(runtime)
65
+
170
66
  transport.connect = () => {
171
67
  return new Promise(resolve => {
172
68
  transport.resolveConnect = resolve
173
69
  transport.log.info('Connecting to transport adapter...')
174
70
 
175
71
  const doConnect = (isTryReconnect) => {
176
- const errorHandler = error => {
72
+ const errorHandler = (error) => {
73
+ // Skip reconnect, if the adapter is disconnecting or an reconnect is in progress.
74
+ if (transport.isDisconnecting || transport.reconnectInProgress) {
75
+ return
76
+ }
77
+
177
78
  transport.log.warn('Connection failed')
178
- transport.log.debug('Error ' + error.message)
79
+ transport.log.debug(error)
80
+
81
+ transport.reconnectInProgress = true
179
82
 
180
83
  if (!error.skipRetry) {
181
84
  setTimeout(() => {
@@ -194,22 +97,28 @@ exports.createTransport = (runtime, adapter) => {
194
97
  }
195
98
 
196
99
  transport.disconnect = () => {
197
- runtime.eventBus.broadcastLocal('$transporter.disconnected', { isGracefull: true })
198
-
100
+ transport.isDisconnecting = true
199
101
  transport.isConnected = false
200
102
  transport.isReady = false
201
103
 
104
+ runtime.eventBus.broadcastLocal('$transporter.disconnected', { isGraceful: true })
105
+
202
106
  stopTimers()
203
107
 
204
- const message = transport.createMessage(MessageTypes.MESSAGE_DISCONNECT)
108
+ const message = createMessage(MessageTypes.MESSAGE_DISCONNECT)
205
109
  return transport.send(message)
206
- .then(() => adapter.close())
110
+ .then(() => {
111
+ adapter.close()
112
+ })
113
+ .then(() => {
114
+ transport.isDisconnecting = false
115
+ })
207
116
  }
208
117
 
209
- transport.setReady = () => {
118
+ transport.setReady = async () => {
210
119
  if (transport.isConnected) {
211
120
  transport.isReady = true
212
- transport.sendNodeInfo()
121
+ await transport.sendNodeInfo()
213
122
  }
214
123
  }
215
124
 
@@ -218,16 +127,18 @@ exports.createTransport = (runtime, adapter) => {
218
127
  * @param {*} sender sender node ID.
219
128
  * @returns {Promise} Promise
220
129
  */
221
- transport.sendNodeInfo = (sender) => {
130
+ transport.sendNodeInfo = async (sender) => {
222
131
  if (!transport.isConnected || !transport.isReady) {
223
132
  return Promise.resolve()
224
133
  }
225
134
 
135
+ // Collect own node info and send it to remote nodes
226
136
  const info = runtime.registry.getLocalNodeInfo()
227
- const message = transport.createMessage(MessageTypes.MESSAGE_INFO, sender, {
137
+ const message = createMessage(MessageTypes.MESSAGE_INFO, sender, {
228
138
  ...info,
229
139
  instanceId: runtime.state.instanceId
230
140
  })
141
+
231
142
  return transport.send(message)
232
143
  }
233
144
 
@@ -236,28 +147,42 @@ exports.createTransport = (runtime, adapter) => {
236
147
  * @param {TransportMessage} message Message to send
237
148
  * @returns {Promise} Promise
238
149
  */
239
- transport.send = (message) => {
150
+ transport.send = async (message) => {
240
151
  transport.statistics.sent.packages = transport.statistics.sent.packages + 1
241
- log.verbose(`Send ${message.type.toUpperCase()} packet to ${message.targetNodeId || 'all nodes'}`)
152
+ transport.log.verbose(`Send ${message.type.toUpperCase()} packet to ${message.targetNodeId || 'all nodes'}`)
242
153
  return adapter.preSend(message)
243
154
  }
244
155
 
245
156
  transport.sendPing = (nodeId) => {
246
- const pingMessage = transport.createMessage(MessageTypes.MESSAGE_PING, nodeId, { dispatchTime: Date.now() })
157
+ const pingMessage = createMessage(MessageTypes.MESSAGE_PING, nodeId, { dispatchTime: Date.now() })
247
158
  return transport.send(pingMessage)
248
159
  }
249
160
 
161
+ /**
162
+ * Send discovery message to all nodes.
163
+ * @returns {void}
164
+ */
250
165
  transport.discoverNodes = () => {
251
- const discoveryMessage = transport.createMessage(MessageTypes.MESSAGE_DISCOVERY)
166
+ const discoveryMessage = createMessage(MessageTypes.MESSAGE_DISCOVERY)
252
167
  transport.send(discoveryMessage)
253
168
  }
254
169
 
255
- transport.discoverNode = (target) => {
256
- const discoveryMessage = transport.createMessage(MessageTypes.MESSAGE_DISCOVERY, target)
257
- transport.send(discoveryMessage)
170
+ /**
171
+ * Send discovery message to all nodes.
172
+ * @param{string} target - Target node ID
173
+ * @returns {Promise<void>} - Promise
174
+ */
175
+ transport.discoverNode = async (target) => {
176
+ const discoveryMessage = createMessage(MessageTypes.MESSAGE_DISCOVERY, target)
177
+ return transport.send(discoveryMessage)
258
178
  }
259
179
 
260
- transport.sendEvent = (context) => {
180
+ /**
181
+ * Send an event
182
+ * @param {Context} context - Context
183
+ * @returns {Promise<void>} - Promise
184
+ */
185
+ transport.sendEvent = async (context) => {
261
186
  const isBroadcast = context.eventType === 'broadcast'
262
187
 
263
188
  const payload = {
@@ -272,12 +197,12 @@ exports.createTransport = (runtime, adapter) => {
272
197
  isBroadcast
273
198
  }
274
199
 
275
- const message = transport.createMessage(MessageTypes.MESSAGE_EVENT, context.endpoint ? context.nodeId : null, payload)
200
+ const message = createMessage(MessageTypes.MESSAGE_EVENT, context.endpoint ? context.nodeId : null, payload)
276
201
  return transport.send(message)
277
202
  }
278
203
 
279
204
  transport.sendBroadcastEvent = (nodeId, eventName, data, groups) => {
280
- log.verbose(`Send ${eventName} to ${nodeId}`)
205
+ transport.log.verbose(`Send ${eventName} to ${nodeId}`)
281
206
 
282
207
  const payload = {
283
208
  data,
@@ -286,18 +211,20 @@ exports.createTransport = (runtime, adapter) => {
286
211
  isBroadcast: true
287
212
  }
288
213
 
289
- const message = transport.createMessage(MessageTypes.MESSAGE_EVENT, nodeId, payload)
290
- transport.send(message)
214
+ const message = createMessage(MessageTypes.MESSAGE_EVENT, nodeId, payload)
215
+ return transport.send(message)
291
216
  }
292
217
 
293
218
  transport.removePendingRequestsById = (requestId) => {
294
219
  pending.requests.delete(requestId)
295
220
  pending.requestStreams.delete(requestId)
296
221
  pending.responseStreams.delete(requestId)
222
+ pending.outboundResponseStreams.delete(requestId)
223
+ pending.outboundRequestStreams.delete(requestId)
297
224
  }
298
225
 
299
226
  transport.removePendingRequestsByNodeId = (nodeId) => {
300
- log.debug(`Remove pending requests for node ${nodeId}.`)
227
+ transport.log.debug(`Remove pending requests for node ${nodeId}.`)
301
228
  pending.requests.forEach((request, requestId) => {
302
229
  if (request.nodeId === nodeId) {
303
230
  pending.requests.delete(requestId)
@@ -309,15 +236,7 @@ exports.createTransport = (runtime, adapter) => {
309
236
  })
310
237
  }
311
238
 
312
- transport.createMessage = (type, targetNodeId, payload) => {
313
- return {
314
- type: type || MessageTypes.MESSAGE_UNKNOWN,
315
- targetNodeId,
316
- payload: payload || {}
317
- }
318
- }
319
-
320
- transport.request = (context) => {
239
+ transport.sendRequest = (context) => {
321
240
  // If the queue size is set, check the queue size and reject the job when the limit is reached.
322
241
  if (runtime.options.transport.maxQueueSize && runtime.options.transport.maxQueueSize < pending.requests.size) {
323
242
  return Promise.reject(new WeaveQueueSizeExceededError({
@@ -328,12 +247,146 @@ exports.createTransport = (runtime, adapter) => {
328
247
  }))
329
248
  }
330
249
 
331
- return new Promise((resolve, reject) => doRequest(context, resolve, reject))
250
+ return new Promise((resolve, reject) => {
251
+ const isStream = utils.isStream(context.options.stream)
252
+
253
+ const request = {
254
+ targetNodeId: context.nodeId,
255
+ action: context.action.name,
256
+ context,
257
+ resolve,
258
+ reject,
259
+ isStream
260
+ }
261
+
262
+ transport.log.debug(`Send request for ${request.action} to node ${request.targetNodeId}.`)
263
+
264
+ pending.requests.set(context.id, request)
265
+
266
+ const payload = {
267
+ id: context.id,
268
+ action: context.action.name,
269
+ data: context.data,
270
+ timeout: context.options.timeout,
271
+ meta: context.meta,
272
+ level: context.level,
273
+ metrics: context.metrics,
274
+ requestId: context.requestId,
275
+ parentId: context.parentId,
276
+ isStream
277
+ }
278
+
279
+ // Handle object mode streams
280
+ if (isStream) {
281
+ const stream = context.options.stream
282
+
283
+ if (!pending.outboundRequestStreams.has(payload.id)) {
284
+ pending.outboundRequestStreams.set(payload.id, stream)
285
+ }
286
+
287
+ if (utils.isStreamObjectMode(stream)) {
288
+ payload.meta = payload.meta || {}
289
+ payload.meta.$isObjectModeStream = true
290
+ }
291
+ payload.sequence = 0
292
+ }
293
+
294
+ const message = createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payload)
295
+
296
+ return transport.send(message)
297
+ .then(() => {
298
+ // send stream
299
+ if (isStream) {
300
+ const stream = context.options.stream
301
+ payload.meta = {}
302
+
303
+ if (utils.isStreamObjectMode(context.options.stream)) {
304
+ payload.meta.$isObjectModeStream = true
305
+ }
306
+
307
+ stream.on('data', data => {
308
+ stream.pause()
309
+ const chunks = []
310
+
311
+ // The chunk is larger than maxBufferSize
312
+ if (data instanceof Buffer && runtime.options.transport.maxChunkSize > 0 && data.length > runtime.options.transport.maxChunkSize) {
313
+ const length = data.length
314
+ let i = 0
315
+ while (i < length) {
316
+ chunks.push(data.slice(i, i += runtime.options.transport.maxChunkSize))
317
+ }
318
+ } else {
319
+ chunks.push(data)
320
+ }
321
+
322
+ // Send chunks from chunk buffer
323
+ for (const chunk of chunks) {
324
+ const payloadCopy = Object.assign({}, payload)
325
+ payloadCopy.sequence = ++payload.sequence
326
+ payloadCopy.chunk = chunk
327
+ payloadCopy.isStream = true
328
+ payload.data = null
329
+
330
+ const message = createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payloadCopy)
331
+ transport.send(message)
332
+ }
333
+
334
+ // resume stream
335
+ stream.resume()
336
+ return
337
+ })
338
+
339
+ stream.on('end', () => {
340
+ const payloadCopy = Object.assign({}, payload)
341
+
342
+ payloadCopy.sequence = ++payload.sequence
343
+ payloadCopy.chunk = null
344
+ payloadCopy.isStream = false
345
+
346
+ pending.outboundRequestStreams.delete(payload.id)
347
+
348
+ const message = createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payloadCopy)
349
+ return transport.send(message)
350
+ })
351
+
352
+ stream.on('error', (error) => {
353
+ const payloadCopy = Object.assign({}, payload)
354
+ payloadCopy.sequence = ++payload.sequence
355
+ payloadCopy.chunk = null
356
+ payloadCopy.isStream = false
357
+
358
+ if (error) {
359
+ payloadCopy.success = false
360
+ payloadCopy.error = getErrorPayload(error)
361
+ }
362
+
363
+ pending.outboundRequestStreams.delete(payload.id)
364
+
365
+ const message = createMessage(MessageTypes.MESSAGE_REQUEST, context.nodeId, payloadCopy)
366
+ return transport.send(message)
367
+ })
368
+ }
369
+ })
370
+ .catch((error) => {
371
+ reject(error)
372
+ })
373
+ })
332
374
  }
333
375
 
334
- transport.response = (target, contextId, data, meta, error) => {
376
+ /**
377
+ * Send response for an open request.
378
+ * @param {string} target - Target node ID
379
+ * @param {string} contextId - Context ID
380
+ * @param {any} data - Data
381
+ * @param {Object} meta - Meta data
382
+ * @param {WeaveError=} error - Error
383
+ * @returns {Promise<void>} - Promise
384
+ */
385
+ transport.sendResponse = (target, contextId, data, meta, error) => {
335
386
  // Check if data is a stream
336
387
  const isStream = utils.isStream(data)
388
+
389
+ // Build response payload
337
390
  const payload = {
338
391
  id: contextId,
339
392
  meta,
@@ -341,20 +394,18 @@ exports.createTransport = (runtime, adapter) => {
341
394
  success: error == null
342
395
  }
343
396
 
397
+ // If an error is occurs, we attach the an error object to the payload.
344
398
  if (error) {
345
- payload.error = {
346
- name: error.name,
347
- message: error.message,
348
- nodeId: error.nodeId || nodeId,
349
- code: error.code,
350
- type: error.type,
351
- stack: error.stack,
352
- data: error.data
353
- }
399
+ payload.error = getErrorPayload(error)
354
400
  }
355
401
 
356
402
  if (isStream) {
357
403
  const stream = data
404
+
405
+ if (!pending.outboundResponseStreams.has(payload.id)) {
406
+ pending.outboundResponseStreams.set(payload.id, stream)
407
+ }
408
+
358
409
  payload.data = null
359
410
  payload.sequence = 0
360
411
  payload.isStream = true
@@ -388,9 +439,9 @@ exports.createTransport = (runtime, adapter) => {
388
439
  payloadCopy.sequence = ++payload.sequence
389
440
 
390
441
  payloadCopy.chunk = chunk
391
- transport.log.debug('Send Stream chunk to ', target)
442
+ transport.log.debug(`Send Stream chunk to ${target}`)
392
443
 
393
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
444
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
394
445
  transport.send(message)
395
446
  }
396
447
 
@@ -406,10 +457,11 @@ exports.createTransport = (runtime, adapter) => {
406
457
  payloadCopy.chunk = null
407
458
  payloadCopy.isStream = false
408
459
 
409
- transport.log.debug('Send end stream chunk to ', target)
460
+ transport.log.debug(`Send end stream chunk to ${target}`)
410
461
 
411
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
462
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
412
463
  transport.send(message)
464
+ pending.outboundResponseStreams.delete(payload.id)
413
465
  })
414
466
 
415
467
  stream.on('error', (error) => {
@@ -421,18 +473,23 @@ exports.createTransport = (runtime, adapter) => {
421
473
 
422
474
  if (error) {
423
475
  payloadCopy.success = false
476
+ payloadCopy.error = getErrorPayload(error)
424
477
  }
425
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
478
+
479
+ transport.log.debug(`Send closing chunk to ${target}`)
480
+
481
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
426
482
  transport.send(message)
483
+ pending.outboundResponseStreams.delete(payload.id)
427
484
  })
428
485
 
429
486
  payload.data = null
430
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload)
487
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload)
431
488
  return transport.send(message)
432
489
  .then(() => stream.resume())
433
490
  }
434
491
 
435
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload)
492
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload)
436
493
  return transport.send(message)
437
494
  }
438
495
 
@@ -510,7 +567,11 @@ exports.createTransport = (runtime, adapter) => {
510
567
  adapter.subscribe(MessageTypes.MESSAGE_PONG, nodeId),
511
568
  adapter.subscribe(MessageTypes.MESSAGE_DISCONNECT),
512
569
  adapter.subscribe(MessageTypes.MESSAGE_HEARTBEAT),
513
- adapter.subscribe(MessageTypes.MESSAGE_EVENT, nodeId)
570
+ adapter.subscribe(MessageTypes.MESSAGE_EVENT, nodeId),
571
+ adapter.subscribe(MessageTypes.MESSAGE_RESPONSE_STREAM_BACKPRESSURE, nodeId),
572
+ adapter.subscribe(MessageTypes.MESSAGE_RESPONSE_STREAM_RESUME, nodeId),
573
+ adapter.subscribe(MessageTypes.MESSAGE_REQUEST_STREAM_BACKPRESSURE, nodeId),
574
+ adapter.subscribe(MessageTypes.MESSAGE_REQUEST_STREAM_RESUME, nodeId)
514
575
  ])
515
576
  }
516
577
 
@@ -549,7 +610,7 @@ exports.createTransport = (runtime, adapter) => {
549
610
  const node = runtime.registry.nodeCollection.localNode
550
611
  node.updateLocalInfo()
551
612
 
552
- log.verbose(`Send heartbeat from ${node.id}`)
613
+ transport.log.verbose(`Send heartbeat from ${node.id}`)
553
614
 
554
615
  const payload = {
555
616
  cpu: node.cpu,
@@ -557,7 +618,7 @@ exports.createTransport = (runtime, adapter) => {
557
618
  sequence: node.sequence
558
619
  }
559
620
 
560
- const message = transport.createMessage(MessageTypes.MESSAGE_HEARTBEAT, null, payload)
621
+ const message = createMessage(MessageTypes.MESSAGE_HEARTBEAT, null, payload)
561
622
  transport.send(message)
562
623
  }
563
624
 
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Error Payload factory
3
+ * @param {import("../types").Runtime} runtime Runtime
4
+ * @returns {function(Error)} Generate error payload object
5
+ */
6
+ const errorPayloadFactory = (runtime) => (error) => {
7
+ return {
8
+ name: error.name,
9
+ message: error.message,
10
+ nodeId: error.nodeId || runtime.nodeId,
11
+ code: error.code,
12
+ type: error.type,
13
+ stack: error.stack,
14
+ data: error.data
15
+ }
16
+ }
17
+
18
+ module.exports = { errorPayloadFactory }