comq 0.9.0 → 0.10.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 (59) hide show
  1. package/.github/dependabot.yml +2 -2
  2. package/.github/workflows/codeql.yml +1 -1
  3. package/.github/workflows/release.yaml +14 -15
  4. package/docs/reply-stream-topology-dark.jpg +0 -0
  5. package/docs/reply-stream-topology-light.jpg +0 -0
  6. package/examples/readme.md +19 -3
  7. package/examples/streams/consumer.js +36 -0
  8. package/examples/streams/producer.js +44 -0
  9. package/features/backpressure.feature +2 -2
  10. package/features/readme.md +2 -2
  11. package/features/rpc.streams.feature +81 -0
  12. package/features/rpc.streams.shards.feature +37 -0
  13. package/features/steps/.test/context.test.js +1 -1
  14. package/features/steps/.test/events.test.js +1 -1
  15. package/features/steps/.types/context.d.ts +9 -1
  16. package/features/steps/common.js +12 -0
  17. package/features/steps/context.js +9 -2
  18. package/features/steps/rpc.js +235 -0
  19. package/lerna.json +1 -2
  20. package/package.json +2 -1
  21. package/readme.md +140 -46
  22. package/source/.io/ReplyPipe.js +158 -0
  23. package/source/.io/ReplyStream.js +142 -0
  24. package/source/.io/const.js +10 -0
  25. package/source/.io/{replies.js → createReplyEmitter.js} +3 -6
  26. package/source/.io/index.js +5 -1
  27. package/source/attributes/failsafe.js +34 -0
  28. package/source/attributes/failsafe.test.js +133 -0
  29. package/source/attributes/index.js +13 -0
  30. package/source/attributes/lazy.js +79 -0
  31. package/source/attributes/lazy.test.js +299 -0
  32. package/source/attributes/memo.js +13 -0
  33. package/source/attributes/memo.test.js +36 -0
  34. package/source/attributes/recall.js +42 -0
  35. package/source/attributes/recall.test.js +92 -0
  36. package/source/attributes/track.js +32 -0
  37. package/source/attributes/track.test.js +133 -0
  38. package/source/channel.js +31 -18
  39. package/source/connect.js +2 -2
  40. package/source/connection.js +4 -11
  41. package/source/emitter.js +13 -0
  42. package/source/encode.js +1 -1
  43. package/source/events.js +2 -2
  44. package/source/io.js +165 -47
  45. package/source/shards/channel.js +13 -10
  46. package/source/shards/connection.js +2 -2
  47. package/test/channel.test.js +3 -1
  48. package/test/encode.test.js +2 -2
  49. package/test/encodings.js +1 -1
  50. package/test/io.diagnostics.test.js +2 -2
  51. package/test/io.request.test.js +4 -23
  52. package/test/shards/channel.test.js +1 -1
  53. package/test/shards/connection.test.js +1 -1
  54. package/types/channel.d.ts +13 -11
  55. package/types/connection.d.ts +3 -3
  56. package/types/diagnostic.d.ts +4 -3
  57. package/types/encoding.d.ts +2 -2
  58. package/types/index.d.ts +4 -4
  59. package/types/io.d.ts +36 -40
@@ -1,8 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { EventEmitter } = require('node:events')
4
3
  const events = require('../events')
5
4
  const channel = require('./channel')
5
+ const emitter = require('../emitter')
6
6
 
7
7
  /**
8
8
  * @implements {comq.Connection}
@@ -11,7 +11,7 @@ class Connection {
11
11
  /** @type {comq.Connection[]} */
12
12
  #connections
13
13
 
14
- #diagnostics = new EventEmitter()
14
+ #diagnostics = emitter.create()
15
15
 
16
16
  /**
17
17
  * @param {comq.Connection[]} connections
@@ -67,7 +67,9 @@ describe('consume', () => {
67
67
  it('should assert queue', async () => {
68
68
  await channel.consume(queue, consumer)
69
69
 
70
- const options = topology.durable ? { durable: true } : { exclusive: true }
70
+ const options = topology.durable
71
+ ? { durable: true }
72
+ : { exclusive: true }
71
73
 
72
74
  expect(chan.assertQueue).toHaveBeenCalledWith(queue, options)
73
75
  })
@@ -11,7 +11,7 @@ it('should be', async () => {
11
11
 
12
12
  it('should throw if encoding is not supported', async () => {
13
13
  const value = 1
14
- const encoding = /** @type {comq.encoding} */ 'wtf/' + generate()
14
+ const encoding = /** @type {comq.Encoding} */ 'wtf/' + generate()
15
15
 
16
16
  expect(() => encode(value, encoding)).toThrow('not supported')
17
17
  })
@@ -34,7 +34,7 @@ it('should encode as msgpack', async () => {
34
34
  expect(buffer).toStrictEqual(packed)
35
35
  })
36
36
 
37
- /** @type {[comq.encoding, string, Buffer][]} */
37
+ /** @type {[comq.Encoding, string, Buffer][]} */
38
38
  const samples = [
39
39
  ['text/plain', 'some string', Buffer.from('some string')]
40
40
  ]
package/test/encodings.js CHANGED
@@ -1,4 +1,4 @@
1
1
  'use strict'
2
2
 
3
- /** @type {comq.encoding[]} */
3
+ /** @type {comq.Encoding[]} */
4
4
  exports.encodings = ['application/msgpack', 'application/json']
@@ -30,7 +30,7 @@ describe.each(['request', 'reply', 'event'])('%s channel events',
30
30
  (type) => {
31
31
  it.each(['flow', 'drain', 'recover', 'discard', 'pause', 'resume'])('should re-emit %s',
32
32
  /**
33
- * @param {comq.diagnostics.event} event
33
+ * @param {comq.diagnostics.Event} event
34
34
  */
35
35
  async (event) => {
36
36
  const listener = /** @type {Function} */ jest.fn()
@@ -56,7 +56,7 @@ describe.each(['request', 'reply', 'event'])('%s channel events',
56
56
 
57
57
  it.each(['open', 'close'])('should re-emit %s from connection',
58
58
  /**
59
- * @param {comq.diagnostics.event} event
59
+ * @param {comq.diagnostics.Event} event
60
60
  */
61
61
  async (event) => {
62
62
  const listener = /** @type {Function} */ jest.fn(() => undefined)
@@ -89,25 +89,6 @@ describe('send', () => {
89
89
  expect(properties.replyTo).toMatch(rx)
90
90
  })
91
91
 
92
- it('should set replyTo using specified formatter', async () => {
93
- jest.clearAllMocks()
94
-
95
- /** @type {comq.encoding} */
96
- const prefix = generate()
97
- const encoding = 'application/json'
98
- const formatter = (replyTo) => prefix + replyTo
99
-
100
- promise = io.request(queue, payload, encoding, formatter)
101
-
102
- await immediate()
103
-
104
- call = requests.send.mock.calls[0]
105
-
106
- const properties = requests.send.mock.calls[0][2]
107
-
108
- expect(properties.replyTo).toMatch(new RegExp(`${prefix}${queue}..[a-z0-9]+`))
109
- })
110
-
111
92
  it('should consume replyTo', async () => {
112
93
  const properties = call[2]
113
94
  const queue = replies.consume.mock.calls[0][0]
@@ -116,7 +97,7 @@ describe('send', () => {
116
97
  })
117
98
 
118
99
  it('should encode message with msgpack by default', async () => {
119
- /** @type {comq.encoding} */
100
+ /** @type {comq.Encoding} */
120
101
  const contentType = 'application/msgpack'
121
102
  const buffer = encode(payload, contentType)
122
103
 
@@ -125,7 +106,7 @@ describe('send', () => {
125
106
  })
126
107
 
127
108
  it('should throw if encoding is not supported', async () => {
128
- const encoding = /** @type {comq.encoding} */ 'wtf/' + generate()
109
+ const encoding = /** @type {comq.Encoding} */ 'wtf/' + generate()
129
110
 
130
111
  await expect(io.request(queue, payload, encoding)).rejects.toThrow('is not supported')
131
112
  })
@@ -153,7 +134,7 @@ describe('send', () => {
153
134
 
154
135
  setImmediate(reply)
155
136
 
156
- await io.request(queue, payload, /** @type {comq.encoding} */ encoding)
137
+ await io.request(queue, payload, /** @type {comq.Encoding} */ encoding)
157
138
 
158
139
  const [, buffer, properties] = requests.send.mock.calls[0]
159
140
 
@@ -244,7 +225,7 @@ describe('reply', () => {
244
225
 
245
226
  it.each(encodings)('should decode %s',
246
227
  /**
247
- * @param {comq.encoding}contentType
228
+ * @param {comq.Encoding}contentType
248
229
  */
249
230
  async (contentType) => {
250
231
  const value = generate()
@@ -289,7 +289,7 @@ describe('seal', () => {
289
289
  })
290
290
 
291
291
  describe('diagnose', () => {
292
- const events = /** @type {comq.diagnostics.event[]} */ ['flow', 'drain', 'recover', 'discard']
292
+ const events = /** @type {comq.diagnostics.Event[]} */ ['flow', 'drain', 'recover', 'discard']
293
293
 
294
294
  /** @type {jest.MockedObject<comq.Channel>[]} */
295
295
  let channels
@@ -106,7 +106,7 @@ describe('createChannel', () => {
106
106
  })
107
107
  })
108
108
 
109
- describe.each(/** @type {comq.diagnostics.event[]} */ ['open', 'close'])('diagnose %s event',
109
+ describe.each(/** @type {comq.diagnostics.Event[]} */ ['open', 'close'])('diagnose %s event',
110
110
  (event) => {
111
111
  it('should re-emit event', async () => {
112
112
  const index = random(connections.length)
@@ -5,7 +5,7 @@ declare namespace comq {
5
5
 
6
6
  namespace channels {
7
7
 
8
- type consumer = (message: Message) => void | Promise<void>
8
+ type Consumer = (message: Message) => void | Promise<void>
9
9
 
10
10
  }
11
11
 
@@ -13,26 +13,28 @@ declare namespace comq {
13
13
  index?: number
14
14
  sharded?: boolean
15
15
 
16
- create(): Promise<void>
16
+ create (): Promise<void>
17
17
 
18
- consume(queue: string, consumer: channels.consumer): Promise<void>
18
+ consume (queue: string, consumer: channels.Consumer): Promise<string>
19
19
 
20
- subscribe(exchange: string, queue: string, consumer: channels.consumer): Promise<void>
20
+ subscribe (exchange: string, queue: string, consumer: channels.Consumer): Promise<void>
21
21
 
22
- send(queue: string, buffer: Buffer, options?: Options.Publish): Promise<void>
22
+ send (queue: string, buffer: Buffer, options?: Options.Publish): Promise<void>
23
23
 
24
- publish(exchange: string, buffer: Buffer, options?: Options.Publish): Promise<void>
24
+ publish (exchange: string, buffer: Buffer, options?: Options.Publish): Promise<void>
25
25
 
26
- fire(queue: string, buffer: Buffer, options?: Options.Publish): Promise<void>
26
+ fire (queue: string, buffer: Buffer, options?: Options.Publish): Promise<boolean>
27
27
 
28
- seal(): Promise<void>
28
+ seal (): Promise<void>
29
29
 
30
- diagnose(event: _diagnostics.event, listener: Function): void
30
+ diagnose (event: _diagnostics.Event, listener: Function): void
31
31
 
32
- recover(connection: Connection): Promise<void>
32
+ forget (event: _diagnostics.Event, listener: Function): void
33
+
34
+ recover (connection: Connection): Promise<void>
33
35
  }
34
36
 
35
37
  }
36
38
 
37
39
  export type Channel = comq.Channel
38
- export type consumer = comq.channels.consumer
40
+ export type Consumer = comq.channels.Consumer
@@ -14,11 +14,11 @@ declare namespace comq {
14
14
 
15
15
  createChannel(type: _topology.type, index: number): Promise<_channel.Channel>
16
16
 
17
- diagnose(event: _diagnostics.event, listener: Function): void
17
+ diagnose(event: _diagnostics.Event, listener: Function): void
18
18
  }
19
19
 
20
- type connect = (...urls: string[]) => Promise<_io.IO>
20
+ type Connect = (...urls: string[]) => Promise<_io.IO>
21
21
 
22
22
  }
23
23
 
24
- export type connect = comq.connect
24
+ export type Connect = comq.Connect
@@ -1,12 +1,13 @@
1
1
  declare namespace comq.diagnostics {
2
2
 
3
- type event = 'open' | 'close' | 'flow' | 'drain' | 'remove' | 'recover' | 'discard' | 'pause' | 'resume'
3
+ type Event = 'open' | 'close' | 'flow' | 'drain' | 'remove' | 'recover' | 'discard' |
4
+ 'pause' | 'resume' | 'return'
4
5
 
5
6
  interface Diagnosable {
6
- diagnose(event: event, listener: Function): void
7
+ diagnose(event: Event, listener: Function): void
7
8
  }
8
9
 
9
10
  }
10
11
 
11
- export type event = comq.diagnostics.event
12
+ export type Event = comq.diagnostics.Event
12
13
  export type Diagnosable = comq.diagnostics.Diagnosable
@@ -1,6 +1,6 @@
1
1
  declare namespace comq {
2
2
 
3
- type encoding =
3
+ type Encoding =
4
4
  'application/msgpack'
5
5
  | 'application/json'
6
6
  | 'application/octet-stream'
@@ -8,4 +8,4 @@ declare namespace comq {
8
8
 
9
9
  }
10
10
 
11
- export type encoding = comq.encoding
11
+ export type Encoding = comq.Encoding
package/types/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { connect } from './connection'
1
+ import { Connect } from './connection'
2
2
 
3
- export type { IO, consumer, producer } from './io'
3
+ export type { IO, Consumer, Producer } from './io'
4
4
 
5
- export const connect: connect
6
- export const assert: connect
5
+ export const connect: Connect
6
+ export const assert: Connect
package/types/io.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { Readable } from 'node:stream'
2
+ import type { EventEmitter } from 'node:events'
2
3
  import * as _diagnostics from './diagnostic'
3
4
  import * as _encoding from './encoding'
4
5
  import * as _topology from './topology'
@@ -6,70 +7,65 @@ import * as _amqp from './amqp'
6
7
 
7
8
  declare namespace comq {
8
9
 
9
- type producer = (message: any) => any | Promise<any>
10
- type consumer = (message: any, headers?: _amqp.Properties) => void | Promise<void>
10
+ type Producer<Input = any, Output = any> = (message: Input) => Output | Promise<Output>
11
+ type Consumer<T = any> = (message: T, headers?: _amqp.Properties) => void | Promise<void>
11
12
 
12
- interface ReplyEmitter {
13
- queue: string
14
-
15
- once(name: string, callback: Function): void
16
-
17
- emit(name: string, value: any): void
13
+ interface ReplyEmitter extends EventEmitter {
14
+ readonly queue: string
15
+ }
18
16
 
19
- clear(): void
17
+ interface Destroyable extends EventEmitter {
18
+ destroy (): void
20
19
  }
21
20
 
22
- type ReplyToPropertyFormatter = (queue: string) => string
21
+ interface Request {
22
+ buffer: Buffer
23
+ emitter: ReplyEmitter
24
+ properties: _amqp.Properties
25
+ reply: any
26
+ }
23
27
 
24
28
  interface IO extends _diagnostics.Diagnosable {
25
- reply(queue: string, produce: producer): Promise<void>
29
+ reply (queue: string, produce: Producer): Promise<void>
30
+
31
+ request<Reply = any, Request = any> (queue: string, payload: Request, encoding?: _encoding.Encoding): Promise<Reply>
26
32
 
27
- request(
28
- queue: string,
29
- payload: any,
30
- encoding?: _encoding.encoding,
31
- replyToFormatter?: ReplyToPropertyFormatter)
32
- : Promise<any>
33
+ request (queue: string, stream: Readable, encoding?: _encoding.Encoding): Promise<Readable>
33
34
 
34
- request(
35
- queue: string,
36
- stream: Readable,
37
- encoding?: _encoding.encoding,
38
- replyToFormatter?: ReplyToPropertyFormatter)
39
- : Promise<Readable>
35
+ fetch (queue: string, payload: any, encoding?: _encoding.Encoding): Promise<Readable>
40
36
 
41
- consume(exchange: string, group: string, consumer: consumer): Promise<void>
37
+ consume<T = any> (exchange: string, group: string, consumer: Consumer<T>): Promise<void>
42
38
 
43
- consume(exchange: string, consumer: consumer): Promise<void>
39
+ consume<T = any> (exchange: string, consumer: Consumer<T>): Promise<void>
44
40
 
45
- emit(exchange: string, payload: any, encoding?: _encoding.encoding): Promise<void>
41
+ emit (exchange: string, payload: any, encoding?: _encoding.Encoding): Promise<void>
46
42
 
47
- emit(exchange: string, payload: any, properties?: _amqp.Properties): Promise<void>
43
+ emit (exchange: string, payload: any, properties?: _amqp.Properties): Promise<void>
48
44
 
49
- emit(exchange: string, stream: Readable, encoding?: _encoding.encoding): Promise<void>
45
+ emit (exchange: string, stream: Readable, encoding?: _encoding.Encoding): Promise<void>
50
46
 
51
- emit(exchange: string, stream: Readable, properties?: _amqp.Properties): Promise<void>
47
+ emit (exchange: string, stream: Readable, properties?: _amqp.Properties): Promise<void>
52
48
 
53
- seal(): Promise<void>
49
+ seal (): Promise<void>
54
50
 
55
- close(): Promise<void>
51
+ close (): Promise<void>
56
52
 
57
- diagnose(event: 'open', listener: (index?: number) => void): void
53
+ diagnose (event: 'open', listener: (index?: number) => void): void
58
54
 
59
- diagnose(event: 'close', listener: (index?: number) => void): void
55
+ diagnose (event: 'close', listener: (index?: number) => void): void
60
56
 
61
- diagnose(event: 'flow', listener: (channel: _topology.type, index?: number) => void): void
57
+ diagnose (event: 'flow', listener: (channel: _topology.type, index?: number) => void): void
62
58
 
63
- diagnose(event: 'drain', listener: (channel: _topology.type, index?: number) => void): void
59
+ diagnose (event: 'drain', listener: (channel: _topology.type, index?: number) => void): void
64
60
 
65
- diagnose(event: 'remove', listener: (index?: number) => void): void
61
+ diagnose (event: 'remove', listener: (index?: number) => void): void
66
62
 
67
- diagnose(event: 'recover', listener: (channel: _topology.type, index?: number) => void): void
63
+ diagnose (event: 'recover', listener: (channel: _topology.type, index?: number) => void): void
68
64
 
69
- diagnose(event: 'discard', listener: (channel: _topology.type, message: any, index?: number) => void): void
65
+ diagnose (event: 'discard', listener: (channel: _topology.type, message: any, index?: number) => void): void
70
66
  }
71
67
  }
72
68
 
73
- export type producer = comq.producer
74
- export type consumer = comq.consumer
69
+ export type Producer = comq.Producer
70
+ export type Consumer = comq.Consumer
75
71
  export type IO = comq.IO