comq 0.15.3 → 0.15.4

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/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.15.4](https://github.com/toa-io/comq/compare/v0.15.3...v0.15.4) (2026-08-16)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * remove request() timeout ([#246](https://github.com/toa-io/comq/issues/246)) ([d432388](https://github.com/toa-io/comq/commit/d4323889bcad867a870d0ed351b92098f6aad551))
7
+
1
8
  ## [0.15.3](https://github.com/toa-io/comq/compare/v0.15.2...v0.15.3) (2026-08-04)
2
9
 
3
10
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "comq",
3
- "version": "0.15.3",
3
+ "version": "0.15.4",
4
4
  "description": "ComQ",
5
5
  "author": {
6
6
  "name": "Artem Gurtovoi",
package/readme.md CHANGED
@@ -103,11 +103,10 @@ await io.reply('add_numbers', ({ a, b }) => (a + b))
103
103
 
104
104
  ## Request
105
105
 
106
- `async IO.request(queue: string, payload: any, encoding?: string, timeout?: number): any`
106
+ `async IO.request(queue: string, payload: any, encoding?: string): any`
107
107
 
108
108
  Send encoded Request message with `replyTo` and `correlationId` properties set and
109
- return decoded Reply content. Optional `timeout` (ms) rejects with `ETIMEDOUT` if no
110
- Reply arrives in time; encoding may be omitted when only `timeout` is passed.
109
+ return decoded Reply content. The promise stays pending until the Reply arrives.
111
110
 
112
111
  On the initial call, queues for Requests and Replies are asserted.
113
112
 
@@ -115,7 +114,6 @@ On the initial call, queues for Requests and Replies are asserted.
115
114
 
116
115
  ```javascript
117
116
  const sum = await io.request('add_numbers', { a: 1, b: 2 })
118
- const sumOrTimeout = await io.request('add_numbers', { a: 1, b: 2 }, 5000)
119
117
  ```
120
118
 
121
119
  ## Consumption
@@ -332,8 +330,7 @@ Outgoing messages are sent to a single connection chosen at random from the shar
332
330
  underlying connection or experience channel [back pressure](#flow-control) on a corresponding channel are removed from
333
331
  the pool until the issue is resolved. Pending messages meeting these conditions are immediately routed among the
334
332
  remaining shards in the pool. If no shards are available, `send` / `publish` / `request` wait until a shard's
335
- connection is re-established (unbounded). For RPC, pass a [`request` timeout](#request) so callers fail with
336
- `ETIMEDOUT` instead of hanging while the pool is empty or a Reply never arrives.
333
+ connection is re-established.
337
334
 
338
335
  Incoming messages are consumed from all shards.
339
336
 
package/source/io.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const stream = require('node:stream')
4
4
  const { EventEmitter } = require('node:events')
5
5
  const { randomBytes } = require('node:crypto')
6
- const { setTimeout: delay } = require('node:timers/promises')
6
+ const { setTimeout } = require('node:timers/promises')
7
7
  const { Promex } = require('promex')
8
8
  const { memo, failsafe, lazy, track } = require('./attributes')
9
9
 
@@ -75,45 +75,24 @@ class IO {
75
75
  /**
76
76
  * @param {string} queue
77
77
  * @param {any | Readable} payload
78
- * @param {comq.Encoding | number} [encoding]
79
- * @param {number} [timeout]
78
+ * @param {comq.Encoding} [encoding]
80
79
  * @returns {Promise<any | Readable>}
81
80
  */
82
- async (queue, payload, encoding, timeout) => {
83
- if (typeof encoding === 'number') {
84
- timeout = encoding
85
- encoding = undefined
86
- }
87
-
81
+ async (queue, payload, encoding) => {
88
82
  if (payload instanceof stream.Readable) {
89
83
  return pipeline(
90
84
  payload,
91
- (payload) => this.request(queue, payload, encoding, timeout),
85
+ (payload) => this.request(queue, payload, encoding),
92
86
  this.#requests
93
87
  )
94
88
  }
95
89
 
96
- const request = this.#createRequest(queue, payload, /** @type {comq.Encoding} */ encoding)
90
+ const request = this.#createRequest(queue, payload, encoding)
97
91
  const reply = this.#createReply(request)
98
92
 
99
- const done = this.#requests.send(queue, request.buffer, request.properties)
100
- .then(() => reply)
101
-
102
- if (typeof timeout !== 'number') return done
93
+ await this.#requests.send(queue, request.buffer, request.properties)
103
94
 
104
- try {
105
- return await Promise.race([
106
- done,
107
- delay(timeout).then(() => Promise.reject(timeoutError(timeout)))
108
- ])
109
- } catch (exception) {
110
- if (exception.code === 'ETIMEDOUT') {
111
- request.emitter.removeAllListeners(request.properties.correlationId)
112
- reply.reject(exception)
113
- }
114
-
115
- throw exception
116
- }
95
+ return reply
117
96
  }))
118
97
 
119
98
  consume = lazy(this, this.#createEventChannel,
@@ -428,7 +407,7 @@ class IO {
428
407
  Even if these messages are lost, the reply stream will be closed anyway,
429
408
  either due to missing heartbeat or the deletion of the stream queue.
430
409
  */
431
- await delay(50)
410
+ await setTimeout(50)
432
411
  }
433
412
 
434
413
  /**
@@ -495,19 +474,6 @@ const DEFAULT = 'application/json'
495
474
 
496
475
  const RETRANSMISSION = /** @type {Error} */ Symbol('retransmission')
497
476
 
498
- /**
499
- * @param {number} timeout
500
- * @returns {Error}
501
- */
502
- function timeoutError (timeout) {
503
- const error = /** @type {Error & { code: string }} */ new Error(
504
- `Request timed out after ${timeout}ms`)
505
-
506
- error.code = 'ETIMEDOUT'
507
-
508
- return error
509
- }
510
-
511
477
  function noop () {}
512
478
 
513
479
  exports.IO = IO
package/types/io.d.ts CHANGED
@@ -27,11 +27,9 @@ declare namespace comq {
27
27
  interface IO extends _diagnostics.Diagnosable {
28
28
  reply(queue: string, produce: Producer): Promise<void>
29
29
 
30
- request<Reply = any, Request = any>(queue: string, payload: Request, encoding?: _encoding.Encoding, timeout?: number): Promise<Reply> | Promise<Readable>
30
+ request<Reply = any, Request = any>(queue: string, payload: Request, encoding?: _encoding.Encoding): Promise<Reply> | Promise<Readable>
31
31
 
32
- request<Reply = any, Request = any>(queue: string, payload: Request, timeout: number): Promise<Reply> | Promise<Readable>
33
-
34
- request(queue: string, stream: Readable, encoding?: _encoding.Encoding, timeout?: number): Promise<Readable>
32
+ request(queue: string, stream: Readable, encoding?: _encoding.Encoding): Promise<Readable>
35
33
 
36
34
  consume<T = any>(exchange: string, group: string, consumer: Consumer<T>): Promise<void>
37
35
 
@@ -1,61 +0,0 @@
1
- 'use strict'
2
-
3
- const { generate } = require('randomstring')
4
-
5
- const mock = require('./connection.mock')
6
- const { IO } = require('../source/io')
7
-
8
- /** @type {comq.IO} */
9
- let io
10
-
11
- /** @type {jest.MockedObject<comq.Connection>} */
12
- let connection
13
-
14
- const queue = generate()
15
- const payload = { [generate()]: generate() }
16
-
17
- beforeEach(() => {
18
- jest.clearAllMocks()
19
-
20
- connection = mock.connection()
21
- io = new IO(connection)
22
- })
23
-
24
- it('should reject when request times out', async () => {
25
- await expect(io.request(queue, payload, 50)).rejects.toMatchObject({
26
- message: expect.stringContaining('timed out'),
27
- code: 'ETIMEDOUT'
28
- })
29
- })
30
-
31
- it('should reject when request times out with encoding', async () => {
32
- await expect(io.request(queue, payload, 'application/json', 50)).rejects.toMatchObject({
33
- code: 'ETIMEDOUT'
34
- })
35
- })
36
-
37
- it('should time out while send is still pending', async () => {
38
- connection.createChannel.mockImplementation(async () => hangingChannel())
39
-
40
- await expect(io.request(queue, payload, 50)).rejects.toMatchObject({
41
- code: 'ETIMEDOUT'
42
- })
43
- })
44
-
45
- /**
46
- * @returns {jest.MockedObject<comq.Channel>}
47
- */
48
- function hangingChannel () {
49
- return /** @type {jest.MockedObject<comq.Channel>} */ ({
50
- sharded: false,
51
- consume: jest.fn(async () => undefined),
52
- send: jest.fn(() => new Promise(() => {})),
53
- deliver: jest.fn(async () => undefined),
54
- fire: jest.fn(async () => undefined),
55
- subscribe: jest.fn(async () => undefined),
56
- publish: jest.fn(async () => undefined),
57
- diagnose: jest.fn(),
58
- seal: jest.fn(async () => undefined),
59
- recover: jest.fn(async () => undefined)
60
- })
61
- }