@weave-js/core 0.10.0 → 0.12.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 (132) hide show
  1. package/.eslintrc.js +1 -1
  2. package/jest.config.js +1 -1
  3. package/lib/ExtendableError.js +33 -0
  4. package/lib/broker/context.js +58 -46
  5. package/lib/broker/{default-options.js → defaultOptions.js} +31 -13
  6. package/lib/broker/index.js +273 -251
  7. package/lib/buildRuntime.js +70 -0
  8. package/lib/cache/adapters/base.js +119 -0
  9. package/lib/cache/adapters/inMemory.js +154 -0
  10. package/lib/cache/adapters/inMemoryLru.js +140 -0
  11. package/lib/cache/adapters/index.js +10 -0
  12. package/lib/cache/getCacheKeyByObject.js +31 -0
  13. package/lib/cache/getPropertyFromDataOrMetadata.js +20 -0
  14. package/lib/cache/lock.js +34 -11
  15. package/lib/constants.js +11 -8
  16. package/lib/{error-handler.js → errorHandler.js} +8 -8
  17. package/lib/errors.js +46 -39
  18. package/lib/helper/{define-action.js → defineAction.js} +5 -3
  19. package/lib/helper/{define-broker-options.js → defineBrokerOptions.js} +4 -2
  20. package/lib/helper/{define-service.js → defineService.js} +6 -3
  21. package/lib/index.js +25 -22
  22. package/lib/logger/base.js +38 -29
  23. package/lib/logger/format/asHumanReadable.js +15 -14
  24. package/lib/logger/format/asJson.js +10 -10
  25. package/lib/logger/format/index.js +3 -3
  26. package/lib/logger/index.js +28 -25
  27. package/lib/logger/levels.js +23 -22
  28. package/lib/logger/tools.js +17 -13
  29. package/lib/logger/utils/colorize.js +23 -23
  30. package/lib/logger/utils/format.js +89 -65
  31. package/lib/metrics/common.js +47 -47
  32. package/lib/metrics/constants.js +44 -44
  33. package/lib/metrics/exporter/base.js +8 -8
  34. package/lib/metrics/exporter/event.js +20 -20
  35. package/lib/metrics/exporter/index.js +15 -15
  36. package/lib/metrics/index.js +2 -2
  37. package/lib/metrics/types/base.js +20 -20
  38. package/lib/metrics/types/counter.js +6 -6
  39. package/lib/metrics/types/gauge.js +24 -24
  40. package/lib/metrics/types/histogram.js +26 -26
  41. package/lib/metrics/types/index.js +6 -6
  42. package/lib/metrics/types/info.js +17 -17
  43. package/lib/middlewares/action-hooks/index.js +82 -34
  44. package/lib/middlewares/bulkhead/index.js +34 -33
  45. package/lib/middlewares/cache/index.js +106 -0
  46. package/lib/middlewares/circuit-breaker/index.js +85 -60
  47. package/lib/middlewares/context-tracker/index.js +39 -39
  48. package/lib/middlewares/error-handler/index.js +14 -14
  49. package/lib/middlewares/index.js +3 -2
  50. package/lib/middlewares/metrics/getMiddlewareWrapper.js +17 -17
  51. package/lib/middlewares/metrics/index.js +29 -29
  52. package/lib/middlewares/retry/index.js +15 -15
  53. package/lib/middlewares/timeout/index.js +18 -18
  54. package/lib/middlewares/tracing/index.js +50 -37
  55. package/lib/middlewares/tracing/tags.js +43 -0
  56. package/lib/middlewares/validator/index.js +32 -32
  57. package/lib/registry/{action-endpoint.js → actionEndpoint.js} +8 -8
  58. package/lib/registry/collections/{action-collection.js → actionCollection.js} +39 -35
  59. package/lib/registry/collections/{endpoint-collection.js → endpointCollection.js} +64 -65
  60. package/lib/registry/collections/{event-collection.js → eventCollection.js} +65 -69
  61. package/lib/registry/collections/{node-collection.js → nodeCollection.js} +45 -45
  62. package/lib/registry/collections/{service-collection.js → serviceCollection.js} +61 -55
  63. package/lib/registry/eventEndpoint.js +26 -0
  64. package/lib/registry/load-balancer/base.js +3 -3
  65. package/lib/registry/load-balancer/index.js +7 -7
  66. package/lib/registry/node.js +27 -27
  67. package/lib/registry/registry.js +147 -163
  68. package/lib/registry/service/parseAction.js +16 -16
  69. package/lib/registry/service/parseEvent.js +18 -18
  70. package/lib/registry/service/reduceMixins.js +10 -10
  71. package/lib/registry/service/service.js +72 -72
  72. package/lib/registry/{service-item.js → serviceItem.js} +21 -21
  73. package/lib/runtime/{init-action-invoker.js → initActionInvoker.js} +21 -21
  74. package/lib/runtime/initCache.js +19 -0
  75. package/lib/runtime/{init-context-factory.js → initContextFactory.js} +22 -22
  76. package/lib/runtime/{init-eventbus.js → initEventbus.js} +54 -53
  77. package/lib/runtime/{init-logger.js → initLogger.js} +11 -11
  78. package/lib/runtime/{init-metrics.js → initMetrics.js} +55 -54
  79. package/lib/runtime/{init-middleware-manager.js → initMiddlewareManager.js} +21 -21
  80. package/lib/runtime/{init-registry.js → initRegistry.js} +6 -6
  81. package/lib/runtime/initServiceManager.js +145 -0
  82. package/lib/runtime/{init-tracing.js → initTracing.js} +25 -24
  83. package/lib/runtime/{init-transport.js → initTransport.js} +5 -5
  84. package/lib/runtime/{init-uuid-factory.js → initUuidFactory.js} +4 -4
  85. package/lib/runtime/{init-validator.js → initValidator.js} +5 -5
  86. package/lib/tracing/collectors/base.js +29 -37
  87. package/lib/tracing/collectors/event.js +33 -33
  88. package/lib/tracing/collectors/index.js +16 -16
  89. package/lib/tracing/span.js +34 -32
  90. package/lib/tracing/time.js +8 -8
  91. package/lib/transport/InboundTransformStream.js +75 -0
  92. package/lib/transport/adapters/adapteBase.js +107 -0
  93. package/lib/transport/adapters/adapters.js +4 -4
  94. package/lib/transport/adapters/dummy/index.js +19 -19
  95. package/lib/transport/adapters/fromURI.js +13 -13
  96. package/lib/transport/adapters/getAdapterByName.js +5 -5
  97. package/lib/transport/adapters/index.js +12 -12
  98. package/lib/transport/adapters/tcp/discovery/codec.js +4 -4
  99. package/lib/transport/adapters/tcp/discovery/index.js +90 -76
  100. package/lib/transport/adapters/tcp/index.js +180 -175
  101. package/lib/transport/adapters/tcp/tcp-messagetypes.js +7 -7
  102. package/lib/transport/adapters/tcp/tcpReader.js +39 -38
  103. package/lib/transport/adapters/tcp/tcpWriteStream.js +26 -26
  104. package/lib/transport/adapters/tcp/tcpWriter.js +58 -58
  105. package/lib/transport/adapters/tcp/utils.js +12 -12
  106. package/lib/transport/createMessage.js +11 -0
  107. package/lib/transport/createTransport.js +377 -334
  108. package/lib/transport/errorPayloadFactory.js +15 -0
  109. package/lib/transport/messageHandlers.js +517 -0
  110. package/lib/transport/messageTypes.js +23 -0
  111. package/lib/types.js +95 -71
  112. package/lib/utils/index.js +2 -2
  113. package/lib/utils/options.js +47 -45
  114. package/lib/utils/{restore-error.js → restoreError.js} +4 -4
  115. package/package.json +6 -12
  116. package/CHANGELOG.md +0 -1334
  117. package/lib/build-runtime.js +0 -70
  118. package/lib/cache/base.js +0 -142
  119. package/lib/cache/index.js +0 -48
  120. package/lib/cache/memory.js +0 -121
  121. package/lib/middlewares/tracing/buildTags.js +0 -17
  122. package/lib/new-errors.js +0 -25
  123. package/lib/registry/event-endpoint.js +0 -26
  124. package/lib/runtime/init-cache.js +0 -10
  125. package/lib/runtime/init-service-manager.js +0 -128
  126. package/lib/tracing/collectors/console.js +0 -34
  127. package/lib/tracing/index.js +0 -3
  128. package/lib/tracing/tracer.js +0 -64
  129. package/lib/transport/adapters/adapter-base.js +0 -92
  130. package/lib/transport/message-handlers.js +0 -448
  131. package/lib/transport/message-types.js +0 -59
  132. package/lib/transport/message.js +0 -16
@@ -1,5 +1,5 @@
1
1
  /*
2
- * Author: Kevin Ries (kevin@fachw3rk.de)
2
+ * Author: Kevin Ries (kevin.ries@fachwerk.io)
3
3
  * -----
4
4
  * Copyright 2021 Fachwerk
5
5
  */
@@ -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
- const { WeaveError, WeaveQueueSizeExceededError } = require('../errors')
16
- const MessageTypes = require('./message-types')
17
- const utils = require('@weave-js/utils')
18
- const createMessageHandler = require('./message-handlers')
16
+ const { WeaveError, WeaveQueueSizeExceededError } = require('../errors');
17
+ const { createMessage } = require('./createMessage');
18
+ const MessageTypes = require('./messageTypes');
19
+ const utils = require('@weave-js/utils');
20
+ const createMessageHandler = require('./messageHandlers');
21
+ const { errorPayloadFactory } = require('./errorPayloadFactory');
19
22
 
20
23
  /**
21
24
  * Create a Transport adapter
@@ -24,140 +27,31 @@ const createMessageHandler = require('./message-handlers')
24
27
  * @returns {Transport} transport
25
28
  */
26
29
  exports.createTransport = (runtime, adapter) => {
27
- const transport = Object.create(null)
28
- const { nodeId, middlewareHandler } = runtime
30
+ const transport = Object.create(null);
31
+ const { nodeId, middlewareHandler, createLogger } = runtime;
29
32
 
30
- let heartbeatTimer
31
- let checkNodesTimer
32
- let checkOfflineNodesTimer
33
- let updateLocalNodeTimer
34
-
35
- const log = runtime.createLogger('TRANSPORT')
33
+ let heartbeatTimer;
34
+ let checkNodesTimer;
35
+ let checkOfflineNodesTimer;
36
+ let updateLocalNodeTimer;
36
37
 
38
+ // Pending action store
37
39
  const pending = {
38
40
  requests: new Map(),
39
41
  requestStreams: new Map(),
40
- responseStreams: new Map()
41
- }
42
-
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')
155
- transport.isConnected = false
156
- transport.isDisconnecting = false
157
- transport.isReady = false
158
- transport.pending = pending
159
- transport.resolveConnect = null
160
- transport.adapterName = adapter.name
42
+ responseStreams: new Map(),
43
+ outboundResponseStreams: new Map(),
44
+ outboundRequestStreams: new Map()
45
+ };
46
+
47
+ transport.log = createLogger('TRANSPORT');
48
+ transport.reconnectInProgress = false;
49
+ transport.isConnected = false;
50
+ transport.isDisconnecting = false;
51
+ transport.isReady = false;
52
+ transport.pending = pending;
53
+ transport.resolveConnect = null;
54
+ transport.adapterName = adapter.name;
161
55
  transport.statistics = {
162
56
  received: {
163
57
  packages: 0
@@ -165,115 +59,131 @@ exports.createTransport = (runtime, adapter) => {
165
59
  sent: {
166
60
  packages: 0
167
61
  }
168
- }
62
+ };
63
+
64
+ const getErrorPayload = errorPayloadFactory(runtime);
169
65
 
170
66
  transport.connect = () => {
171
67
  return new Promise(resolve => {
172
- transport.resolveConnect = resolve
173
- transport.log.info('Connecting to transport adapter...')
68
+ transport.resolveConnect = resolve;
69
+ transport.log.info('Connecting to transport adapter...');
174
70
 
175
71
  const doConnect = (isTryReconnect) => {
176
- let reconnectInProgress = false
177
-
178
72
  const errorHandler = (error) => {
179
73
  // Skip reconnect, if the adapter is disconnecting or an reconnect is in progress.
180
- if (transport.isDisconnecting || reconnectInProgress) {
181
- return
74
+ if (transport.isDisconnecting || transport.reconnectInProgress) {
75
+ return;
182
76
  }
183
77
 
184
- transport.log.warn('Connection failed')
185
- transport.log.debug(error)
78
+ transport.log.warn('Connection failed');
79
+ transport.log.debug(error);
186
80
 
187
- reconnectInProgress = true
81
+ transport.reconnectInProgress = true;
188
82
 
189
83
  if (!error.skipRetry) {
190
84
  setTimeout(() => {
191
- transport.log.info('Reconnecting')
192
- doConnect(true)
193
- }, 5 * 1000)
85
+ transport.log.info('Reconnecting');
86
+ doConnect(true);
87
+ }, 5 * 1000);
194
88
  }
195
- }
89
+ };
196
90
  return adapter
197
91
  .connect(isTryReconnect, errorHandler)
198
- .catch(errorHandler)
199
- }
92
+ .catch(errorHandler);
93
+ };
200
94
 
201
- doConnect(false)
202
- })
203
- }
95
+ doConnect(false);
96
+ });
97
+ };
204
98
 
205
99
  transport.disconnect = () => {
206
- transport.isDisconnecting = true
207
- transport.isConnected = false
208
- transport.isReady = false
100
+ transport.isDisconnecting = true;
101
+ transport.isConnected = false;
102
+ transport.isReady = false;
209
103
 
210
- runtime.eventBus.broadcastLocal('$transporter.disconnected', { isGracefull: true })
104
+ runtime.eventBus.broadcastLocal('$transporter.disconnected', { isGraceful: true });
211
105
 
212
- stopTimers()
106
+ stopTimers();
213
107
 
214
- const message = transport.createMessage(MessageTypes.MESSAGE_DISCONNECT)
108
+ const message = createMessage(MessageTypes.MESSAGE_DISCONNECT);
215
109
  return transport.send(message)
216
110
  .then(() => {
217
- adapter.close()
111
+ adapter.close();
218
112
  })
219
113
  .then(() => {
220
- transport.isDisconnecting = false
221
- })
222
- }
114
+ transport.isDisconnecting = false;
115
+ });
116
+ };
223
117
 
224
- transport.setReady = () => {
118
+ transport.setReady = async () => {
225
119
  if (transport.isConnected) {
226
- transport.isReady = true
227
- transport.sendNodeInfo()
120
+ transport.isReady = true;
121
+ await transport.sendNodeInfo();
228
122
  }
229
- }
123
+ };
230
124
 
231
125
  /**
232
126
  * Send node information.
233
127
  * @param {*} sender sender node ID.
234
128
  * @returns {Promise} Promise
235
129
  */
236
- transport.sendNodeInfo = (sender) => {
130
+ transport.sendNodeInfo = async (sender) => {
237
131
  if (!transport.isConnected || !transport.isReady) {
238
- return Promise.resolve()
132
+ return Promise.resolve();
239
133
  }
240
134
 
241
- const info = runtime.registry.getLocalNodeInfo()
242
- const message = transport.createMessage(MessageTypes.MESSAGE_INFO, sender, {
135
+ // Collect own node info and send it to remote nodes
136
+ const info = runtime.registry.getLocalNodeInfo();
137
+ const message = createMessage(MessageTypes.MESSAGE_INFO, sender, {
243
138
  ...info,
244
139
  instanceId: runtime.state.instanceId
245
- })
246
- return transport.send(message)
247
- }
140
+ });
141
+
142
+ return transport.send(message);
143
+ };
248
144
 
249
145
  /**
250
146
  * Send a message
251
147
  * @param {TransportMessage} message Message to send
252
148
  * @returns {Promise} Promise
253
149
  */
254
- transport.send = (message) => {
255
- transport.statistics.sent.packages = transport.statistics.sent.packages + 1
256
- log.verbose(`Send ${message.type.toUpperCase()} packet to ${message.targetNodeId || 'all nodes'}`)
257
- return adapter.preSend(message)
258
- }
150
+ transport.send = async (message) => {
151
+ transport.statistics.sent.packages = transport.statistics.sent.packages + 1;
152
+ transport.log.verbose(`Send ${message.type.toUpperCase()} packet to ${message.targetNodeId || 'all nodes'}`);
153
+ return adapter.preSend(message);
154
+ };
259
155
 
260
156
  transport.sendPing = (nodeId) => {
261
- const pingMessage = transport.createMessage(MessageTypes.MESSAGE_PING, nodeId, { dispatchTime: Date.now() })
262
- return transport.send(pingMessage)
263
- }
157
+ const pingMessage = createMessage(MessageTypes.MESSAGE_PING, nodeId, { dispatchTime: Date.now() });
158
+ return transport.send(pingMessage);
159
+ };
264
160
 
161
+ /**
162
+ * Send discovery message to all nodes.
163
+ * @returns {void}
164
+ */
265
165
  transport.discoverNodes = () => {
266
- const discoveryMessage = transport.createMessage(MessageTypes.MESSAGE_DISCOVERY)
267
- transport.send(discoveryMessage)
268
- }
166
+ const discoveryMessage = createMessage(MessageTypes.MESSAGE_DISCOVERY);
167
+ transport.send(discoveryMessage);
168
+ };
269
169
 
270
- transport.discoverNode = (target) => {
271
- const discoveryMessage = transport.createMessage(MessageTypes.MESSAGE_DISCOVERY, target)
272
- transport.send(discoveryMessage)
273
- }
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);
178
+ };
274
179
 
275
- transport.sendEvent = (context) => {
276
- const isBroadcast = context.eventType === 'broadcast'
180
+ /**
181
+ * Send an event
182
+ * @param {Context} context - Context
183
+ * @returns {Promise<void>} - Promise
184
+ */
185
+ transport.sendEvent = async (context) => {
186
+ const isBroadcast = context.eventType === 'broadcast';
277
187
 
278
188
  const payload = {
279
189
  data: context.data,
@@ -285,54 +195,48 @@ exports.createTransport = (runtime, adapter) => {
285
195
  requestId: context.requestId,
286
196
  parentId: context.parentId,
287
197
  isBroadcast
288
- }
198
+ };
289
199
 
290
- const message = transport.createMessage(MessageTypes.MESSAGE_EVENT, context.endpoint ? context.nodeId : null, payload)
291
- return transport.send(message)
292
- }
200
+ const message = createMessage(MessageTypes.MESSAGE_EVENT, context.endpoint ? context.nodeId : null, payload);
201
+ return transport.send(message);
202
+ };
293
203
 
294
204
  transport.sendBroadcastEvent = (nodeId, eventName, data, groups) => {
295
- log.verbose(`Send ${eventName} to ${nodeId}`)
205
+ transport.log.verbose(`Send ${eventName} to ${nodeId}`);
296
206
 
297
207
  const payload = {
298
208
  data,
299
209
  eventName,
300
210
  groups,
301
211
  isBroadcast: true
302
- }
212
+ };
303
213
 
304
- const message = transport.createMessage(MessageTypes.MESSAGE_EVENT, nodeId, payload)
305
- transport.send(message)
306
- }
214
+ const message = createMessage(MessageTypes.MESSAGE_EVENT, nodeId, payload);
215
+ return transport.send(message);
216
+ };
307
217
 
308
218
  transport.removePendingRequestsById = (requestId) => {
309
- pending.requests.delete(requestId)
310
- pending.requestStreams.delete(requestId)
311
- pending.responseStreams.delete(requestId)
312
- }
219
+ pending.requests.delete(requestId);
220
+ pending.requestStreams.delete(requestId);
221
+ pending.responseStreams.delete(requestId);
222
+ pending.outboundResponseStreams.delete(requestId);
223
+ pending.outboundRequestStreams.delete(requestId);
224
+ };
313
225
 
314
226
  transport.removePendingRequestsByNodeId = (nodeId) => {
315
- log.debug(`Remove pending requests for node ${nodeId}.`)
227
+ transport.log.debug(`Remove pending requests for node ${nodeId}.`);
316
228
  pending.requests.forEach((request, requestId) => {
317
229
  if (request.nodeId === nodeId) {
318
- pending.requests.delete(requestId)
230
+ pending.requests.delete(requestId);
319
231
  }
320
- request.reject(new WeaveError(`Remove pending requests for node ${nodeId}.`))
232
+ request.reject(new WeaveError(`Remove pending requests for node ${nodeId}.`));
321
233
 
322
- pending.requestStreams.delete(requestId)
323
- pending.responseStreams.delete(requestId)
324
- })
325
- }
234
+ pending.requestStreams.delete(requestId);
235
+ pending.responseStreams.delete(requestId);
236
+ });
237
+ };
326
238
 
327
- transport.createMessage = (type, targetNodeId, payload) => {
328
- return {
329
- type: type || MessageTypes.MESSAGE_UNKNOWN,
330
- targetNodeId,
331
- payload: payload || {}
332
- }
333
- }
334
-
335
- transport.request = (context) => {
239
+ transport.sendRequest = (context) => {
336
240
  // If the queue size is set, check the queue size and reject the job when the limit is reached.
337
241
  if (runtime.options.transport.maxQueueSize && runtime.options.transport.maxQueueSize < pending.requests.size) {
338
242
  return Promise.reject(new WeaveQueueSizeExceededError({
@@ -340,15 +244,147 @@ exports.createTransport = (runtime, adapter) => {
340
244
  limit: runtime.options.transport.maxQueueSize,
341
245
  nodeId: context.nodeId,
342
246
  size: pending.requests.size
343
- }))
247
+ }));
344
248
  }
345
249
 
346
- return new Promise((resolve, reject) => doRequest(context, resolve, reject))
347
- }
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
+ }
348
333
 
349
- transport.response = (target, contextId, data, meta, error) => {
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
+ });
374
+ };
375
+
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) => {
350
386
  // Check if data is a stream
351
- const isStream = utils.isStream(data)
387
+ const isStream = utils.isStream(data);
352
388
 
353
389
  // Build response payload
354
390
  const payload = {
@@ -356,165 +392,168 @@ exports.createTransport = (runtime, adapter) => {
356
392
  meta,
357
393
  data,
358
394
  success: error == null
359
- }
395
+ };
360
396
 
361
397
  // If an error is occurs, we attach the an error object to the payload.
362
398
  if (error) {
363
- payload.error = {
364
- name: error.name,
365
- message: error.message,
366
- nodeId: error.nodeId || nodeId,
367
- code: error.code,
368
- type: error.type,
369
- stack: error.stack,
370
- data: error.data
371
- }
399
+ payload.error = getErrorPayload(error);
372
400
  }
373
401
 
374
402
  if (isStream) {
375
- const stream = data
376
- payload.data = null
377
- payload.sequence = 0
378
- payload.isStream = true
403
+ const stream = data;
404
+
405
+ if (!pending.outboundResponseStreams.has(payload.id)) {
406
+ pending.outboundResponseStreams.set(payload.id, stream);
407
+ }
408
+
409
+ payload.data = null;
410
+ payload.sequence = 0;
411
+ payload.isStream = true;
379
412
 
380
413
  if (utils.isStreamObjectMode(data)) {
381
- payload.meta = payload.meta || {}
382
- payload.meta.$isObjectModeStream = true
414
+ payload.meta = payload.meta || {};
415
+ payload.meta.$isObjectModeStream = true;
383
416
  }
384
417
 
385
- stream.pause()
386
- transport.log.debug('Send new stream chunk to ', target)
418
+ stream.pause();
419
+ transport.log.debug('Send new stream chunk to ', target);
387
420
 
388
421
  stream.on('data', data => {
389
- stream.pause()
390
- const chunks = []
422
+ stream.pause();
423
+ const chunks = [];
391
424
 
392
425
  // chunk is larger than maxBufferSize
393
426
  if (data instanceof Buffer && runtime.options.transport.maxChunkSize > 0 && data.length > runtime.options.transport.maxChunkSize) {
394
- const length = data.length
395
- let i = 0
427
+ const length = data.length;
428
+ let i = 0;
396
429
  while (i < length) {
397
- chunks.push(data.slice(i, i += runtime.options.transport.maxChunkSize))
430
+ chunks.push(data.slice(i, i += runtime.options.transport.maxChunkSize));
398
431
  }
399
432
  } else {
400
- chunks.push(data)
433
+ chunks.push(data);
401
434
  }
402
435
 
403
436
  // Send chunks from chunk buffer
404
437
  for (const chunk of chunks) {
405
- const payloadCopy = Object.assign({}, payload)
406
- payloadCopy.sequence = ++payload.sequence
438
+ const payloadCopy = Object.assign({}, payload);
439
+ payloadCopy.sequence = ++payload.sequence;
407
440
 
408
- payloadCopy.chunk = chunk
409
- transport.log.debug('Send Stream chunk to ', target)
441
+ payloadCopy.chunk = chunk;
442
+ transport.log.debug(`Send Stream chunk to ${target}`);
410
443
 
411
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
412
- transport.send(message)
444
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy);
445
+ transport.send(message);
413
446
  }
414
447
 
415
448
  // resume stream
416
- stream.resume()
417
- return
418
- })
449
+ stream.resume();
450
+ return;
451
+ });
419
452
 
420
453
  stream.on('end', () => {
421
- const payloadCopy = Object.assign({}, payload)
454
+ const payloadCopy = Object.assign({}, payload);
422
455
 
423
- payloadCopy.sequence = ++payload.sequence
424
- payloadCopy.chunk = null
425
- payloadCopy.isStream = false
456
+ payloadCopy.sequence = ++payload.sequence;
457
+ payloadCopy.chunk = null;
458
+ payloadCopy.isStream = false;
426
459
 
427
- transport.log.debug('Send end stream chunk to ', target)
460
+ transport.log.debug(`Send end stream chunk to ${target}`);
428
461
 
429
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
430
- transport.send(message)
431
- })
462
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy);
463
+ transport.send(message);
464
+ pending.outboundResponseStreams.delete(payload.id);
465
+ });
432
466
 
433
467
  stream.on('error', (error) => {
434
- const payloadCopy = Object.assign({}, payload)
468
+ const payloadCopy = Object.assign({}, payload);
435
469
 
436
- payloadCopy.sequence = ++payload.sequence
437
- payloadCopy.isStream = false
438
- payloadCopy.chunk = null
470
+ payloadCopy.sequence = ++payload.sequence;
471
+ payloadCopy.isStream = false;
472
+ payloadCopy.chunk = null;
439
473
 
440
474
  if (error) {
441
- payloadCopy.success = false
475
+ payloadCopy.success = false;
476
+ payloadCopy.error = getErrorPayload(error);
442
477
  }
443
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy)
444
- transport.send(message)
445
- })
446
478
 
447
- payload.data = null
448
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload)
479
+ transport.log.debug(`Send closing chunk to ${target}`);
480
+
481
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payloadCopy);
482
+ transport.send(message);
483
+ pending.outboundResponseStreams.delete(payload.id);
484
+ });
485
+
486
+ payload.data = null;
487
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload);
449
488
  return transport.send(message)
450
- .then(() => stream.resume())
489
+ .then(() => stream.resume());
451
490
  }
452
491
 
453
- const message = transport.createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload)
454
- return transport.send(message)
455
- }
492
+ const message = createMessage(MessageTypes.MESSAGE_RESPONSE, target, payload);
493
+ return transport.send(message);
494
+ };
456
495
 
457
496
  const onConnect = ({ wasReconnect = false, useHeartbeatTimer = true, useRemoteNodeCheckTimer = true, useOfflineCheckTimer = true }) =>
458
497
  Promise.resolve()
459
498
  .then(() => {
460
499
  if (!wasReconnect) {
461
- return makeSubscriptions()
500
+ return makeSubscriptions();
462
501
  }
463
502
  })
464
503
  .then(() => transport.discoverNodes())
465
504
  .then(() => utils.promiseDelay(Promise.resolve(), 500))
466
505
  .then(() => {
467
- transport.isConnected = true
506
+ transport.isConnected = true;
468
507
 
469
- runtime.eventBus.broadcastLocal('$transporter.connected', { wasReconnect })
508
+ runtime.eventBus.broadcastLocal('$transporter.connected', { wasReconnect });
470
509
 
471
510
  if (transport.resolveConnect) {
472
- transport.resolveConnect()
473
- transport.resolveConnect = null
511
+ transport.resolveConnect();
512
+ transport.resolveConnect = null;
474
513
  }
475
514
  })
476
515
  .then(() => {
477
- startUpdateLocalNodeTimer()
516
+ startUpdateLocalNodeTimer();
478
517
 
479
518
  if (useHeartbeatTimer) {
480
- startHeartbeatTimer()
519
+ startHeartbeatTimer();
481
520
  }
482
521
 
483
522
  if (useRemoteNodeCheckTimer) {
484
- startRemoteNodeCheckTimer()
523
+ startRemoteNodeCheckTimer();
485
524
  }
486
525
 
487
526
  if (useOfflineCheckTimer) {
488
- startOfflineNodeCheckTimer()
527
+ startOfflineNodeCheckTimer();
489
528
  }
490
- })
529
+ });
491
530
 
492
531
  const onDisconnect = () => {
493
532
  Promise.resolve()
494
533
  .then(() => {
495
- transport.isConnected = false
496
- runtime.bus.emit('$transporter.disconnected')
534
+ transport.isConnected = false;
535
+ runtime.bus.emit('$transporter.disconnected');
497
536
  })
498
537
  .then(() => {
499
- stopTimers()
500
- })
501
- }
538
+ stopTimers();
539
+ });
540
+ };
502
541
 
503
- let messageHandler = createMessageHandler(runtime, transport)
542
+ let messageHandler = createMessageHandler(runtime, transport);
504
543
 
505
544
  // Wrap message handler for middlewares
506
- messageHandler = middlewareHandler.wrapMethod('transportMessageHandler', messageHandler, transport)
545
+ messageHandler = middlewareHandler.wrapMethod('transportMessageHandler', messageHandler, transport);
507
546
 
508
547
  adapter.init(runtime, transport)
509
548
  .then(() => {
510
- adapter.bus.on('$adapter.connected', onConnect)
511
- adapter.bus.on('$adapter.disconnected', onDisconnect)
512
- adapter.bus.on('$adapter.message', messageHandler)
513
- })
549
+ adapter.bus.on('$adapter.connected', onConnect);
550
+ adapter.bus.on('$adapter.disconnected', onDisconnect);
551
+ adapter.bus.on('$adapter.message', messageHandler);
552
+ });
514
553
 
515
- transport.send = middlewareHandler.wrapMethod('transportSend', transport.send, transport)
554
+ transport.send = middlewareHandler.wrapMethod('transportSend', transport.send, transport);
516
555
 
517
- return transport
556
+ return transport;
518
557
 
519
558
  function makeSubscriptions () {
520
559
  return Promise.all([
@@ -528,81 +567,85 @@ exports.createTransport = (runtime, adapter) => {
528
567
  adapter.subscribe(MessageTypes.MESSAGE_PONG, nodeId),
529
568
  adapter.subscribe(MessageTypes.MESSAGE_DISCONNECT),
530
569
  adapter.subscribe(MessageTypes.MESSAGE_HEARTBEAT),
531
- adapter.subscribe(MessageTypes.MESSAGE_EVENT, nodeId)
532
- ])
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)
575
+ ]);
533
576
  }
534
577
 
535
578
  function startHeartbeatTimer () {
536
- heartbeatTimer = setInterval(() => sendHeartbeat(), runtime.options.transport.heartbeatInterval)
537
- heartbeatTimer.unref()
579
+ heartbeatTimer = setInterval(() => sendHeartbeat(), runtime.options.transport.heartbeatInterval);
580
+ heartbeatTimer.unref();
538
581
  }
539
582
 
540
583
  function startRemoteNodeCheckTimer () {
541
- checkNodesTimer = setInterval(() => checkRemoteNodes(), runtime.options.transport.heartbeatTimeout)
542
- checkNodesTimer.unref()
584
+ checkNodesTimer = setInterval(() => checkRemoteNodes(), runtime.options.transport.heartbeatTimeout);
585
+ checkNodesTimer.unref();
543
586
  }
544
587
 
545
588
  function startOfflineNodeCheckTimer () {
546
- checkOfflineNodesTimer = setInterval(() => checkOfflineNodes(), runtime.options.transport.offlineNodeCheckInterval)
547
- checkOfflineNodesTimer.unref()
589
+ checkOfflineNodesTimer = setInterval(() => checkOfflineNodes(), runtime.options.transport.offlineNodeCheckInterval);
590
+ checkOfflineNodesTimer.unref();
548
591
  }
549
592
 
550
593
  function startUpdateLocalNodeTimer () {
551
594
  updateLocalNodeTimer = setInterval(() => {
552
- const node = runtime.registry.nodeCollection.localNode
553
- node.updateLocalInfo(true)
554
- }, runtime.options.transport.localNodeUpdateInterval)
595
+ const node = runtime.registry.nodeCollection.localNode;
596
+ node.updateLocalInfo(true);
597
+ }, runtime.options.transport.localNodeUpdateInterval);
555
598
 
556
- updateLocalNodeTimer.unref()
599
+ updateLocalNodeTimer.unref();
557
600
  }
558
601
 
559
602
  function stopTimers () {
560
- clearInterval(heartbeatTimer)
561
- clearInterval(checkNodesTimer)
562
- clearInterval(checkOfflineNodesTimer)
563
- clearInterval(updateLocalNodeTimer)
603
+ clearInterval(heartbeatTimer);
604
+ clearInterval(checkNodesTimer);
605
+ clearInterval(checkOfflineNodesTimer);
606
+ clearInterval(updateLocalNodeTimer);
564
607
  }
565
608
 
566
609
  function sendHeartbeat () {
567
- const node = runtime.registry.nodeCollection.localNode
568
- node.updateLocalInfo()
610
+ const node = runtime.registry.nodeCollection.localNode;
611
+ node.updateLocalInfo();
569
612
 
570
- log.verbose(`Send heartbeat from ${node.id}`)
613
+ transport.log.verbose(`Send heartbeat from ${node.id}`);
571
614
 
572
615
  const payload = {
573
616
  cpu: node.cpu,
574
617
  cpuSequence: node.cpuSequence,
575
618
  sequence: node.sequence
576
- }
619
+ };
577
620
 
578
- const message = transport.createMessage(MessageTypes.MESSAGE_HEARTBEAT, null, payload)
579
- transport.send(message)
621
+ const message = createMessage(MessageTypes.MESSAGE_HEARTBEAT, null, payload);
622
+ transport.send(message);
580
623
  }
581
624
 
582
625
  function checkRemoteNodes () {
583
- const now = Date.now()
626
+ const now = Date.now();
584
627
  runtime.registry.nodeCollection.list({ withServices: true }).forEach(node => {
585
628
  if (node.isLocal || !node.isAvailable) {
586
- return
629
+ return;
587
630
  }
588
631
 
589
632
  if (now - (node.lastHeartbeatTime || 0) > runtime.options.transport.heartbeatTimeout) {
590
- runtime.registry.nodeDisconnected(node.id, true)
633
+ runtime.registry.nodeDisconnected(node.id, true);
591
634
  }
592
- })
635
+ });
593
636
  }
594
637
 
595
638
  // Removes the node after a given time from the registry.
596
639
  function checkOfflineNodes () {
597
- const now = Date.now()
640
+ const now = Date.now();
598
641
  runtime.registry.nodeCollection.list({}).forEach(node => {
599
642
  if (node.isLocal || node.isAvailable) {
600
- return
643
+ return;
601
644
  }
602
645
 
603
646
  if ((now - node.offlineTime) > runtime.options.transport.maxOfflineTime) {
604
- runtime.registry.removeNode(node.id)
647
+ runtime.registry.removeNode(node.id);
605
648
  }
606
- })
649
+ });
607
650
  }
608
- }
651
+ };