pp-command-bus 1.4.0 → 2.0.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 (195) hide show
  1. package/README.md +402 -1113
  2. package/dist/command-bus/command-bus.spec.js +144 -370
  3. package/dist/command-bus/command-bus.spec.js.map +1 -1
  4. package/dist/command-bus/command.d.ts +23 -5
  5. package/dist/command-bus/command.js +20 -34
  6. package/dist/command-bus/command.js.map +1 -1
  7. package/dist/command-bus/config/command-bus-config.d.ts +75 -21
  8. package/dist/command-bus/config/command-bus-config.js +99 -58
  9. package/dist/command-bus/config/command-bus-config.js.map +1 -1
  10. package/dist/command-bus/config/command-bus-config.spec.js +174 -100
  11. package/dist/command-bus/config/command-bus-config.spec.js.map +1 -1
  12. package/dist/command-bus/index.d.ts +39 -52
  13. package/dist/command-bus/index.js +133 -126
  14. package/dist/command-bus/index.js.map +1 -1
  15. package/dist/command-bus/logging/command-logger.d.ts +2 -0
  16. package/dist/command-bus/logging/command-logger.js +7 -0
  17. package/dist/command-bus/logging/command-logger.js.map +1 -1
  18. package/dist/command-bus/logging/command-logger.spec.js +49 -14
  19. package/dist/command-bus/logging/command-logger.spec.js.map +1 -1
  20. package/dist/command-bus/serialization/index.d.ts +6 -0
  21. package/dist/command-bus/serialization/index.js +9 -0
  22. package/dist/command-bus/serialization/index.js.map +1 -0
  23. package/dist/command-bus/serialization/msgpack-serializer.d.ts +26 -0
  24. package/dist/command-bus/serialization/msgpack-serializer.js +70 -0
  25. package/dist/command-bus/serialization/msgpack-serializer.js.map +1 -0
  26. package/dist/command-bus/serialization/msgpack-serializer.spec.js +223 -0
  27. package/dist/command-bus/serialization/msgpack-serializer.spec.js.map +1 -0
  28. package/dist/command-bus/serialization/serializer.interface.d.ts +21 -0
  29. package/dist/command-bus/serialization/serializer.interface.js +3 -0
  30. package/dist/command-bus/serialization/serializer.interface.js.map +1 -0
  31. package/dist/command-bus/transport/consumer-loop.d.ts +45 -0
  32. package/dist/command-bus/transport/consumer-loop.js +90 -0
  33. package/dist/command-bus/transport/consumer-loop.js.map +1 -0
  34. package/dist/command-bus/transport/consumer-loop.spec.js +216 -0
  35. package/dist/command-bus/transport/consumer-loop.spec.js.map +1 -0
  36. package/dist/command-bus/transport/index.d.ts +21 -0
  37. package/dist/command-bus/transport/index.js +23 -0
  38. package/dist/command-bus/transport/index.js.map +1 -0
  39. package/dist/command-bus/transport/message-processor.d.ts +59 -0
  40. package/dist/command-bus/transport/message-processor.js +111 -0
  41. package/dist/command-bus/transport/message-processor.js.map +1 -0
  42. package/dist/command-bus/transport/message-processor.spec.js +185 -0
  43. package/dist/command-bus/transport/message-processor.spec.js.map +1 -0
  44. package/dist/command-bus/transport/pending-recovery.d.ts +54 -0
  45. package/dist/command-bus/transport/pending-recovery.js +139 -0
  46. package/dist/command-bus/transport/pending-recovery.js.map +1 -0
  47. package/dist/command-bus/transport/pending-recovery.spec.js +176 -0
  48. package/dist/command-bus/transport/pending-recovery.spec.js.map +1 -0
  49. package/dist/command-bus/transport/redis-codec.d.ts +24 -0
  50. package/dist/command-bus/transport/redis-codec.js +33 -0
  51. package/dist/command-bus/transport/redis-codec.js.map +1 -0
  52. package/dist/command-bus/transport/redis-codec.spec.js +53 -0
  53. package/dist/command-bus/transport/redis-codec.spec.js.map +1 -0
  54. package/dist/command-bus/transport/redis-streams-transport.d.ts +91 -0
  55. package/dist/command-bus/transport/redis-streams-transport.js +134 -0
  56. package/dist/command-bus/transport/redis-streams-transport.js.map +1 -0
  57. package/dist/command-bus/transport/redis-streams-transport.spec.js +420 -0
  58. package/dist/command-bus/transport/redis-streams-transport.spec.js.map +1 -0
  59. package/dist/command-bus/transport/rpc-handler.d.ts +39 -0
  60. package/dist/command-bus/transport/rpc-handler.js +87 -0
  61. package/dist/command-bus/transport/rpc-handler.js.map +1 -0
  62. package/dist/command-bus/transport/rpc-handler.spec.js +157 -0
  63. package/dist/command-bus/transport/rpc-handler.spec.js.map +1 -0
  64. package/dist/command-bus/transport/stream-consumer.d.ts +89 -0
  65. package/dist/command-bus/transport/stream-consumer.js +181 -0
  66. package/dist/command-bus/transport/stream-consumer.js.map +1 -0
  67. package/dist/command-bus/transport/stream-consumer.spec.js +284 -0
  68. package/dist/command-bus/transport/stream-consumer.spec.js.map +1 -0
  69. package/dist/command-bus/transport/stream-producer.d.ts +23 -0
  70. package/dist/command-bus/transport/stream-producer.js +70 -0
  71. package/dist/command-bus/transport/stream-producer.js.map +1 -0
  72. package/dist/command-bus/transport/stream-producer.spec.js +125 -0
  73. package/dist/command-bus/transport/stream-producer.spec.js.map +1 -0
  74. package/dist/command-bus/transport/transport.interface.d.ts +87 -0
  75. package/dist/command-bus/transport/transport.interface.js +3 -0
  76. package/dist/command-bus/transport/transport.interface.js.map +1 -0
  77. package/dist/command-bus/types/index.d.ts +9 -80
  78. package/dist/examples/rpc-throughput.demo.js +24 -22
  79. package/dist/examples/rpc-throughput.demo.js.map +1 -1
  80. package/dist/examples/rpc.demo.js +47 -53
  81. package/dist/examples/rpc.demo.js.map +1 -1
  82. package/dist/index.d.ts +8 -5
  83. package/dist/index.js +6 -4
  84. package/dist/index.js.map +1 -1
  85. package/dist/pp-command-bus-2.0.0.tgz +0 -0
  86. package/dist/shared/redis/connection-pool.d.ts +54 -0
  87. package/dist/shared/redis/connection-pool.js +117 -0
  88. package/dist/shared/redis/connection-pool.js.map +1 -0
  89. package/dist/shared/redis/connection-pool.spec.js +114 -0
  90. package/dist/shared/redis/connection-pool.spec.js.map +1 -0
  91. package/dist/shared/redis/index.d.ts +5 -3
  92. package/dist/shared/redis/index.js +6 -4
  93. package/dist/shared/redis/index.js.map +1 -1
  94. package/dist/shared/redis/rpc-connection-pool.d.ts +61 -0
  95. package/dist/shared/redis/rpc-connection-pool.js +154 -0
  96. package/dist/shared/redis/rpc-connection-pool.js.map +1 -0
  97. package/dist/shared/redis/rpc-connection-pool.spec.d.ts +1 -0
  98. package/dist/shared/redis/rpc-connection-pool.spec.js +173 -0
  99. package/dist/shared/redis/rpc-connection-pool.spec.js.map +1 -0
  100. package/dist/shared/types.d.ts +0 -4
  101. package/dist/shared/utils/error-utils.d.ts +8 -0
  102. package/dist/shared/utils/error-utils.js +14 -0
  103. package/dist/shared/utils/error-utils.js.map +1 -0
  104. package/package.json +12 -12
  105. package/dist/command-bus/config/auto-config-optimizer.d.ts +0 -35
  106. package/dist/command-bus/config/auto-config-optimizer.js +0 -52
  107. package/dist/command-bus/config/auto-config-optimizer.js.map +0 -1
  108. package/dist/command-bus/config/auto-config-optimizer.spec.js +0 -42
  109. package/dist/command-bus/config/auto-config-optimizer.spec.js.map +0 -1
  110. package/dist/command-bus/job/index.d.ts +0 -6
  111. package/dist/command-bus/job/index.js +0 -15
  112. package/dist/command-bus/job/index.js.map +0 -1
  113. package/dist/command-bus/job/job-options-builder.d.ts +0 -21
  114. package/dist/command-bus/job/job-options-builder.js +0 -58
  115. package/dist/command-bus/job/job-options-builder.js.map +0 -1
  116. package/dist/command-bus/job/job-options-builder.spec.js +0 -156
  117. package/dist/command-bus/job/job-options-builder.spec.js.map +0 -1
  118. package/dist/command-bus/job/job-processor.d.ts +0 -39
  119. package/dist/command-bus/job/job-processor.js +0 -203
  120. package/dist/command-bus/job/job-processor.js.map +0 -1
  121. package/dist/command-bus/job/job-processor.spec.js +0 -437
  122. package/dist/command-bus/job/job-processor.spec.js.map +0 -1
  123. package/dist/command-bus/queue/index.d.ts +0 -5
  124. package/dist/command-bus/queue/index.js +0 -13
  125. package/dist/command-bus/queue/index.js.map +0 -1
  126. package/dist/command-bus/queue/queue-manager.d.ts +0 -56
  127. package/dist/command-bus/queue/queue-manager.js +0 -163
  128. package/dist/command-bus/queue/queue-manager.js.map +0 -1
  129. package/dist/command-bus/queue/queue-manager.spec.js +0 -371
  130. package/dist/command-bus/queue/queue-manager.spec.js.map +0 -1
  131. package/dist/command-bus/rpc/index.d.ts +0 -11
  132. package/dist/command-bus/rpc/index.js +0 -19
  133. package/dist/command-bus/rpc/index.js.map +0 -1
  134. package/dist/command-bus/rpc/payload-compression.service.d.ts +0 -51
  135. package/dist/command-bus/rpc/payload-compression.service.js +0 -218
  136. package/dist/command-bus/rpc/payload-compression.service.js.map +0 -1
  137. package/dist/command-bus/rpc/payload-compression.service.spec.js +0 -379
  138. package/dist/command-bus/rpc/payload-compression.service.spec.js.map +0 -1
  139. package/dist/command-bus/rpc/rpc-coordinator.d.ts +0 -96
  140. package/dist/command-bus/rpc/rpc-coordinator.js +0 -500
  141. package/dist/command-bus/rpc/rpc-coordinator.js.map +0 -1
  142. package/dist/command-bus/rpc/rpc-coordinator.spec.js +0 -622
  143. package/dist/command-bus/rpc/rpc-coordinator.spec.js.map +0 -1
  144. package/dist/command-bus/rpc/rpc-job-cancellation.service.d.ts +0 -82
  145. package/dist/command-bus/rpc/rpc-job-cancellation.service.js +0 -180
  146. package/dist/command-bus/rpc/rpc-job-cancellation.service.js.map +0 -1
  147. package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js +0 -286
  148. package/dist/command-bus/rpc/rpc-job-cancellation.service.spec.js.map +0 -1
  149. package/dist/command-bus/worker/index.d.ts +0 -10
  150. package/dist/command-bus/worker/index.js +0 -19
  151. package/dist/command-bus/worker/index.js.map +0 -1
  152. package/dist/command-bus/worker/worker-benchmark.d.ts +0 -71
  153. package/dist/command-bus/worker/worker-benchmark.js +0 -203
  154. package/dist/command-bus/worker/worker-benchmark.js.map +0 -1
  155. package/dist/command-bus/worker/worker-benchmark.spec.js +0 -310
  156. package/dist/command-bus/worker/worker-benchmark.spec.js.map +0 -1
  157. package/dist/command-bus/worker/worker-metrics-collector.d.ts +0 -98
  158. package/dist/command-bus/worker/worker-metrics-collector.js +0 -242
  159. package/dist/command-bus/worker/worker-metrics-collector.js.map +0 -1
  160. package/dist/command-bus/worker/worker-orchestrator.d.ts +0 -70
  161. package/dist/command-bus/worker/worker-orchestrator.js +0 -339
  162. package/dist/command-bus/worker/worker-orchestrator.js.map +0 -1
  163. package/dist/command-bus/worker/worker-orchestrator.spec.js +0 -712
  164. package/dist/command-bus/worker/worker-orchestrator.spec.js.map +0 -1
  165. package/dist/examples/auto-config.demo.d.ts +0 -9
  166. package/dist/examples/auto-config.demo.js +0 -106
  167. package/dist/examples/auto-config.demo.js.map +0 -1
  168. package/dist/examples/rpc-compression.demo.d.ts +0 -5
  169. package/dist/examples/rpc-compression.demo.js +0 -363
  170. package/dist/examples/rpc-compression.demo.js.map +0 -1
  171. package/dist/examples/rpc-resilience.demo.d.ts +0 -11
  172. package/dist/examples/rpc-resilience.demo.js +0 -235
  173. package/dist/examples/rpc-resilience.demo.js.map +0 -1
  174. package/dist/pp-command-bus-1.4.0.tgz +0 -0
  175. package/dist/shared/config/base-config.d.ts +0 -54
  176. package/dist/shared/config/base-config.js +0 -114
  177. package/dist/shared/config/base-config.js.map +0 -1
  178. package/dist/shared/config/base-config.spec.js +0 -204
  179. package/dist/shared/config/base-config.spec.js.map +0 -1
  180. package/dist/shared/config/index.d.ts +0 -1
  181. package/dist/shared/config/index.js +0 -9
  182. package/dist/shared/config/index.js.map +0 -1
  183. package/dist/shared/redis/redis-connection-factory.d.ts +0 -66
  184. package/dist/shared/redis/redis-connection-factory.js +0 -113
  185. package/dist/shared/redis/redis-connection-factory.js.map +0 -1
  186. /package/dist/command-bus/{config/auto-config-optimizer.spec.d.ts → serialization/msgpack-serializer.spec.d.ts} +0 -0
  187. /package/dist/command-bus/{job/job-options-builder.spec.d.ts → transport/consumer-loop.spec.d.ts} +0 -0
  188. /package/dist/command-bus/{job/job-processor.spec.d.ts → transport/message-processor.spec.d.ts} +0 -0
  189. /package/dist/command-bus/{queue/queue-manager.spec.d.ts → transport/pending-recovery.spec.d.ts} +0 -0
  190. /package/dist/command-bus/{rpc/payload-compression.service.spec.d.ts → transport/redis-codec.spec.d.ts} +0 -0
  191. /package/dist/command-bus/{rpc/rpc-coordinator.spec.d.ts → transport/redis-streams-transport.spec.d.ts} +0 -0
  192. /package/dist/command-bus/{rpc/rpc-job-cancellation.service.spec.d.ts → transport/rpc-handler.spec.d.ts} +0 -0
  193. /package/dist/command-bus/{worker/worker-benchmark.spec.d.ts → transport/stream-consumer.spec.d.ts} +0 -0
  194. /package/dist/command-bus/{worker/worker-orchestrator.spec.d.ts → transport/stream-producer.spec.d.ts} +0 -0
  195. /package/dist/shared/{config/base-config.spec.d.ts → redis/connection-pool.spec.d.ts} +0 -0
@@ -0,0 +1,284 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const stream_consumer_1 = require("./stream-consumer");
13
+ const redis_codec_1 = require("./redis-codec");
14
+ /**
15
+ * Mock helpers
16
+ */
17
+ function createMockLogger() {
18
+ return {
19
+ log: jest.fn(),
20
+ error: jest.fn(),
21
+ warn: jest.fn(),
22
+ debug: jest.fn(),
23
+ };
24
+ }
25
+ function createMockSerializer() {
26
+ return {
27
+ serialize: jest.fn((data) => Buffer.from(JSON.stringify(data))),
28
+ deserialize: jest.fn((buf) => JSON.parse(buf.toString())),
29
+ };
30
+ }
31
+ function createMockConnection() {
32
+ const pendingRejects = [];
33
+ return {
34
+ xadd: jest.fn().mockResolvedValue('1700000000000-0'),
35
+ xreadgroup: jest.fn().mockImplementation(() => new Promise((_resolve, reject) => {
36
+ pendingRejects.push(reject);
37
+ })),
38
+ xack: jest.fn().mockResolvedValue(1),
39
+ xgroup: jest.fn().mockResolvedValue('OK'),
40
+ xtrim: jest.fn().mockResolvedValue(0),
41
+ on: jest.fn(),
42
+ disconnect: jest.fn().mockImplementation(() => {
43
+ const disconnectError = new Error('Connection is closed.');
44
+ while (pendingRejects.length > 0) {
45
+ const reject = pendingRejects.shift();
46
+ if (reject)
47
+ reject(disconnectError);
48
+ }
49
+ }),
50
+ pipeline: jest.fn().mockReturnValue({
51
+ xack: jest.fn().mockReturnThis(),
52
+ xtrim: jest.fn().mockReturnThis(),
53
+ exec: jest.fn().mockResolvedValue([
54
+ [null, 1],
55
+ [null, 0],
56
+ ]),
57
+ }),
58
+ };
59
+ }
60
+ function createMockPool() {
61
+ const conn = createMockConnection();
62
+ return {
63
+ pool: {
64
+ next: jest.fn().mockReturnValue(conn),
65
+ createDedicated: jest.fn().mockReturnValue(conn),
66
+ },
67
+ conn,
68
+ };
69
+ }
70
+ describe('StreamConsumer', () => {
71
+ let consumer;
72
+ let logger;
73
+ let serializer;
74
+ let mockConn;
75
+ let mockPool;
76
+ const rpcRespond = jest.fn().mockResolvedValue(undefined);
77
+ beforeEach(() => {
78
+ jest.clearAllMocks();
79
+ logger = createMockLogger();
80
+ serializer = createMockSerializer();
81
+ const poolMock = createMockPool();
82
+ mockPool = poolMock.pool;
83
+ mockConn = poolMock.conn;
84
+ consumer = new stream_consumer_1.StreamConsumer({
85
+ pool: mockPool,
86
+ serializer,
87
+ logger,
88
+ consumerId: 'test-consumer',
89
+ batchSize: 10,
90
+ concurrency: 5,
91
+ maxRetained: 10000,
92
+ rpcRespond,
93
+ staleThreshold: 100, // Niski threshold dla testów (100ms)
94
+ sweepInterval: 0, // Brak throttle w testach — sweep na każdą wiadomość
95
+ });
96
+ });
97
+ afterEach(() => __awaiter(void 0, void 0, void 0, function* () {
98
+ consumer.running = false;
99
+ consumer.closeConnections();
100
+ yield consumer.awaitLoops();
101
+ }));
102
+ describe('processMessage()', () => {
103
+ it('powinien przetworzyć wiadomość i wywołać handler', () => __awaiter(void 0, void 0, void 0, function* () {
104
+ const handler = jest.fn().mockResolvedValue('ok');
105
+ const testPayload = redis_codec_1.RedisCodec.encode(Buffer.from('test-data'));
106
+ const fields = ['data', testPayload];
107
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-1', fields, handler);
108
+ expect(handler).toHaveBeenCalledTimes(1);
109
+ expect(handler).toHaveBeenCalledWith(expect.any(Buffer));
110
+ }));
111
+ it('powinien zdeduplikować wiadomości przetwarzane równolegle', () => __awaiter(void 0, void 0, void 0, function* () {
112
+ let resolveHandler;
113
+ const handlerPromise = new Promise((resolve) => {
114
+ resolveHandler = resolve;
115
+ });
116
+ const handler = jest.fn().mockReturnValue(handlerPromise);
117
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
118
+ // Rozpocznij pierwsze przetwarzanie (nie kończy się)
119
+ const firstProcessing = consumer.processMessage('cmd:Test', 'workers', 'msg-dup', fields, handler);
120
+ // Drugie przetwarzanie tego samego ID — powinno być pominięte
121
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-dup', fields, handler);
122
+ // Handler powinien być wywołany tylko RAZ
123
+ expect(handler).toHaveBeenCalledTimes(1);
124
+ // Cleanup
125
+ resolveHandler();
126
+ yield firstProcessing;
127
+ }));
128
+ it('powinien XACK wiadomość po przetworzeniu', () => __awaiter(void 0, void 0, void 0, function* () {
129
+ const handler = jest.fn().mockResolvedValue('ok');
130
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
131
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-1', fields, handler);
132
+ expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-1');
133
+ }));
134
+ it('powinien obsłużyć wiadomość z pustym fields array (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
135
+ const handler = jest.fn();
136
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-empty', [], handler);
137
+ // Handler NIE powinien być wywołany
138
+ expect(handler).not.toHaveBeenCalled();
139
+ // Powinien zalogować warning
140
+ expect(logger.warn).toHaveBeenCalledWith('Nieprawidłowa wiadomość — brak danych', expect.objectContaining({ messageId: 'msg-empty' }));
141
+ // Powinien XACK (żeby wiadomość nie wracała)
142
+ expect(mockConn.xack).toHaveBeenCalledWith('cmd:Test', 'workers', 'msg-empty');
143
+ }));
144
+ it('powinien obsłużyć wiadomość z jednym elementem w fields (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
145
+ const handler = jest.fn();
146
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-odd', ['key-only'], handler);
147
+ expect(handler).not.toHaveBeenCalled();
148
+ expect(logger.warn).toHaveBeenCalledWith('Nieprawidłowa wiadomość — brak danych', expect.objectContaining({ messageId: 'msg-odd' }));
149
+ }));
150
+ it('powinien obsłużyć wiadomość bez pola data (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
151
+ const handler = jest.fn();
152
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-nodata', ['other', 'value'], handler);
153
+ expect(handler).not.toHaveBeenCalled();
154
+ expect(logger.warn).toHaveBeenCalledWith('Wiadomość bez pola data', expect.objectContaining({ messageId: 'msg-nodata' }));
155
+ }));
156
+ it('powinien obsłużyć nieparzystą liczbę elementów w fields (H4)', () => __awaiter(void 0, void 0, void 0, function* () {
157
+ const handler = jest.fn().mockResolvedValue('ok');
158
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data')), 'extra-key'];
159
+ // Nie powinien crashować — bounds check w pętli
160
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-odd-fields', fields, handler);
161
+ expect(handler).toHaveBeenCalledTimes(1);
162
+ }));
163
+ it('powinien zalogować błąd gdy handler rzuci wyjątek', () => __awaiter(void 0, void 0, void 0, function* () {
164
+ const handler = jest.fn().mockRejectedValue(new Error('Handler error'));
165
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
166
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-err', fields, handler);
167
+ expect(logger.error).toHaveBeenCalledWith('Błąd przetwarzania wiadomości', expect.objectContaining({
168
+ messageId: 'msg-err',
169
+ error: 'Handler error',
170
+ }));
171
+ }));
172
+ it('powinien usunąć messageId z processing po zakończeniu (nawet przy błędzie)', () => __awaiter(void 0, void 0, void 0, function* () {
173
+ const handler = jest.fn().mockRejectedValue(new Error('fail'));
174
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
175
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-cleanup', fields, handler);
176
+ // Po zakończeniu messageId powinien być usunięty z processing
177
+ expect(consumer['processing'].has('msg-cleanup')).toBe(false);
178
+ }));
179
+ });
180
+ describe('stale processing sweep (H1)', () => {
181
+ it('powinien usunąć stale wpisy z processing Map', () => __awaiter(void 0, void 0, void 0, function* () {
182
+ // Dodaj stale wpis ręcznie (timestamp w przeszłości)
183
+ consumer['processing'].set('msg-stale', Date.now() - 200); // 200ms > 100ms threshold
184
+ const handler = jest.fn().mockResolvedValue('ok');
185
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
186
+ // processMessage wywołuje sweepStaleProcessing na początku
187
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-new', fields, handler);
188
+ // Stale wpis powinien być usunięty
189
+ expect(consumer['processing'].has('msg-stale')).toBe(false);
190
+ // Powinien zalogować warning
191
+ expect(logger.warn).toHaveBeenCalledWith('Usunięto stale wpis z processing Map', expect.objectContaining({ messageId: 'msg-stale' }));
192
+ }));
193
+ it('powinien pozwolić na retry wiadomości po sweep', () => __awaiter(void 0, void 0, void 0, function* () {
194
+ const handler = jest.fn().mockResolvedValue('ok');
195
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
196
+ // Dodaj stale wpis
197
+ consumer['processing'].set('msg-retry', Date.now() - 200);
198
+ // Po sweep, ta sama wiadomość powinna być przetworzona ponownie
199
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-retry', fields, handler);
200
+ expect(handler).toHaveBeenCalledTimes(1);
201
+ }));
202
+ it('nie powinien usuwać świeżych wpisów z processing Map', () => __awaiter(void 0, void 0, void 0, function* () {
203
+ // Dodaj świeży wpis (timestamp teraz)
204
+ consumer['processing'].set('msg-fresh', Date.now());
205
+ const handler = jest.fn().mockResolvedValue('ok');
206
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
207
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-other', fields, handler);
208
+ // Świeży wpis powinien zostać
209
+ expect(consumer['processing'].has('msg-fresh')).toBe(true);
210
+ }));
211
+ });
212
+ describe('XTRIM per-stream (H2)', () => {
213
+ it('powinien zliczać XTRIM niezależnie per strumień', () => __awaiter(void 0, void 0, void 0, function* () {
214
+ const handler = jest.fn().mockResolvedValue('ok');
215
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
216
+ // 9 wiadomości na stream A — nie powinno triggerować XTRIM (batchSize=10)
217
+ for (let i = 0; i < 9; i++) {
218
+ yield consumer.processMessage('cmd:StreamA', 'workers', `a-${i}`, fields, handler);
219
+ }
220
+ // 1 wiadomość na stream B
221
+ yield consumer.processMessage('cmd:StreamB', 'workers', 'b-0', fields, handler);
222
+ // Pipeline (XACK+XTRIM) NIE powinien być wywołany — żaden stream nie osiągnął batchSize
223
+ // Stream A: 9, Stream B: 1
224
+ // Pipeline jest wywoływany dopiero przy batchSize (10)
225
+ const xackCalls = mockConn.xack.mock.calls;
226
+ expect(xackCalls.length).toBe(10);
227
+ // 10. wiadomość na stream A — powinno triggerować XTRIM tylko dla stream A
228
+ yield consumer.processMessage('cmd:StreamA', 'workers', 'a-9', fields, handler);
229
+ // Pipeline powinien być wywołany dla stream A
230
+ expect(mockConn.pipeline).toHaveBeenCalled();
231
+ }));
232
+ it('powinien wykonać XTRIM co batchSize wiadomości', () => __awaiter(void 0, void 0, void 0, function* () {
233
+ const handler = jest.fn().mockResolvedValue('ok');
234
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('data'))];
235
+ // Wyślij dokładnie batchSize (10) wiadomości
236
+ for (let i = 0; i < 10; i++) {
237
+ yield consumer.processMessage('cmd:Test', 'workers', `msg-${i}`, fields, handler);
238
+ }
239
+ // Pipeline powinien być wywołany dokładnie raz (10. wiadomość)
240
+ expect(mockConn.pipeline).toHaveBeenCalledTimes(1);
241
+ }));
242
+ });
243
+ describe('RPC detection', () => {
244
+ it('powinien wykryć RPC wiadomość przez marker i odpowiedzieć', () => __awaiter(void 0, void 0, void 0, function* () {
245
+ const handler = jest.fn().mockResolvedValue('result-42');
246
+ const envelope = {
247
+ commandData: redis_codec_1.RedisCodec.encode(Buffer.from('cmd-data')),
248
+ rpc: { correlationId: 'corr-1', responseKey: 'rpc:res:corr-1' },
249
+ };
250
+ const envelopePayload = redis_codec_1.RedisCodec.encode(Buffer.from(JSON.stringify(envelope)));
251
+ const fields = ['data', envelopePayload, 'rpc', '1'];
252
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-rpc', fields, handler);
253
+ expect(handler).toHaveBeenCalledTimes(1);
254
+ expect(rpcRespond).toHaveBeenCalledWith('rpc:res:corr-1', expect.any(Buffer), 60);
255
+ }));
256
+ it('powinien przetworzyć zwykłą komendę bez rpcRespond', () => __awaiter(void 0, void 0, void 0, function* () {
257
+ const handler = jest.fn().mockResolvedValue('ok');
258
+ const fields = ['data', redis_codec_1.RedisCodec.encode(Buffer.from('normal-data'))];
259
+ yield consumer.processMessage('cmd:Test', 'workers', 'msg-normal', fields, handler);
260
+ expect(handler).toHaveBeenCalledTimes(1);
261
+ expect(rpcRespond).not.toHaveBeenCalled();
262
+ }));
263
+ });
264
+ describe('consume()', () => {
265
+ it('powinien utworzyć consumer group i rozpocząć nasłuchiwanie', () => __awaiter(void 0, void 0, void 0, function* () {
266
+ const handler = jest.fn();
267
+ yield consumer.consume('cmd:Test', 'workers', handler);
268
+ expect(mockConn.xgroup).toHaveBeenCalledWith('CREATE', 'cmd:Test', 'workers', '0', 'MKSTREAM');
269
+ expect(consumer.consumerCount).toBe(1);
270
+ expect(consumer['consumerLoopPromises'].length).toBe(1);
271
+ }));
272
+ it('powinien obsłużyć BUSYGROUP (grupa już istnieje)', () => __awaiter(void 0, void 0, void 0, function* () {
273
+ mockConn.xgroup.mockRejectedValueOnce(new Error('BUSYGROUP Consumer Group name already exists'));
274
+ const handler = jest.fn();
275
+ // Nie powinien rzucić błędu
276
+ yield consumer.consume('cmd:Test', 'workers', handler);
277
+ }));
278
+ it('powinien rzucić błąd dla innych błędów xgroup', () => __awaiter(void 0, void 0, void 0, function* () {
279
+ mockConn.xgroup.mockRejectedValueOnce(new Error('NOPERM access denied'));
280
+ yield expect(consumer.consume('cmd:Test', 'workers', jest.fn())).rejects.toThrow('NOPERM access denied');
281
+ }));
282
+ });
283
+ });
284
+ //# sourceMappingURL=stream-consumer.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-consumer.spec.js","sourceRoot":"","sources":["../../../src/command-bus/transport/stream-consumer.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type RedisConnectionPool from '../../shared/redis/connection-pool';
2
+ import type { IStreamProducer } from './transport.interface';
3
+ /**
4
+ * Producent strumienia Redis — odpowiada za dodawanie wiadomości do strumieni
5
+ * Jedna odpowiedzialność: enqueue (XADD) z kodowaniem RedisCodec
6
+ */
7
+ export declare class StreamProducer implements IStreamProducer {
8
+ private readonly pool;
9
+ constructor(pool: RedisConnectionPool);
10
+ /**
11
+ * Dodaje komendę do strumienia (fire-and-forget)
12
+ * XADD = 1 natywne polecenie Redis, DragonflyDB przetwarza wielowątkowo
13
+ */
14
+ enqueue(streamName: string, data: Buffer): Promise<string>;
15
+ /**
16
+ * Dodaje wiele komend w jednym pipeline
17
+ * DragonflyDB przetwarza pipeline równolegle na wielu wątkach
18
+ */
19
+ enqueueBatch(entries: Array<{
20
+ streamName: string;
21
+ data: Buffer;
22
+ }>): Promise<string[]>;
23
+ }
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.StreamProducer = void 0;
13
+ const redis_codec_1 = require("./redis-codec");
14
+ /**
15
+ * Producent strumienia Redis — odpowiada za dodawanie wiadomości do strumieni
16
+ * Jedna odpowiedzialność: enqueue (XADD) z kodowaniem RedisCodec
17
+ */
18
+ class StreamProducer {
19
+ constructor(pool) {
20
+ this.pool = pool;
21
+ }
22
+ /**
23
+ * Dodaje komendę do strumienia (fire-and-forget)
24
+ * XADD = 1 natywne polecenie Redis, DragonflyDB przetwarza wielowątkowo
25
+ */
26
+ enqueue(streamName, data) {
27
+ return __awaiter(this, void 0, void 0, function* () {
28
+ const conn = this.pool.next();
29
+ const messageId = yield conn.xadd(streamName, '*', 'data', redis_codec_1.RedisCodec.encode(data));
30
+ return messageId;
31
+ });
32
+ }
33
+ /**
34
+ * Dodaje wiele komend w jednym pipeline
35
+ * DragonflyDB przetwarza pipeline równolegle na wielu wątkach
36
+ */
37
+ enqueueBatch(entries) {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ var _a;
40
+ if (entries.length === 0)
41
+ return [];
42
+ const conn = this.pool.next();
43
+ const pipeline = conn.pipeline();
44
+ for (const entry of entries) {
45
+ pipeline.xadd(entry.streamName, '*', 'data', redis_codec_1.RedisCodec.encode(entry.data));
46
+ }
47
+ const results = yield pipeline.exec();
48
+ if (!results)
49
+ return [];
50
+ // H3: Wykrywanie pipeline errors — zapobiega silent data loss
51
+ const failedIndices = [];
52
+ const messageIds = [];
53
+ for (let i = 0; i < results.length; i++) {
54
+ const [error, value] = results[i];
55
+ if (error) {
56
+ failedIndices.push(i);
57
+ }
58
+ else {
59
+ messageIds.push((_a = value) !== null && _a !== void 0 ? _a : '');
60
+ }
61
+ }
62
+ if (failedIndices.length > 0) {
63
+ throw new Error(`Błąd pipeline XADD — ${failedIndices.length}/${results.length} operacji nie powiodło się (indeksy: ${failedIndices.join(', ')})`);
64
+ }
65
+ return messageIds;
66
+ });
67
+ }
68
+ }
69
+ exports.StreamProducer = StreamProducer;
70
+ //# sourceMappingURL=stream-producer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-producer.js","sourceRoot":"","sources":["../../../src/command-bus/transport/stream-producer.ts"],"names":[],"mappings":";;;;;;;;;;;;AAEA,+CAA2C;AAE3C;;;GAGG;AACH,MAAa,cAAc;IACzB,YAA6B,IAAyB;QAAzB,SAAI,GAAJ,IAAI,CAAqB;IAAG,CAAC;IAE1D;;;OAGG;IACU,OAAO,CAAC,UAAkB,EAAE,IAAY;;YACnD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,wBAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;YACpF,OAAO,SAAmB,CAAC;QAC7B,CAAC;KAAA;IAED;;;OAGG;IACU,YAAY,CACvB,OAAoD;;;YAEpD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,EAAE,CAAC;YAEpC,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YAC9B,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,EAAE,CAAC;YAEjC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,EAAE,wBAAU,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YAC9E,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACtC,IAAI,CAAC,OAAO;gBAAE,OAAO,EAAE,CAAC;YAExB,8DAA8D;YAC9D,MAAM,aAAa,GAAa,EAAE,CAAC;YACnC,MAAM,UAAU,GAAa,EAAE,CAAC;YAEhC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACxC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;gBAClC,IAAI,KAAK,EAAE,CAAC;oBACV,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;gBACxB,CAAC;qBAAM,CAAC;oBACN,UAAU,CAAC,IAAI,CAAC,MAAC,KAAgB,mCAAI,EAAE,CAAC,CAAC;gBAC3C,CAAC;YACH,CAAC;YAED,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CACb,wBAAwB,aAAa,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,wCAAwC,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAClI,CAAC;YACJ,CAAC;YAED,OAAO,UAAU,CAAC;QACpB,CAAC;KAAA;CACF;AArDD,wCAqDC"}
@@ -0,0 +1,125 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const stream_producer_1 = require("./stream-producer");
13
+ const redis_codec_1 = require("./redis-codec");
14
+ /**
15
+ * Mock puli połączeń Redis
16
+ */
17
+ function createMockPool() {
18
+ const mockPipeline = {
19
+ xadd: jest.fn().mockReturnThis(),
20
+ exec: jest.fn().mockResolvedValue(null),
21
+ };
22
+ const mockConn = {
23
+ xadd: jest.fn().mockResolvedValue('1700000000000-0'),
24
+ pipeline: jest.fn().mockReturnValue(mockPipeline),
25
+ };
26
+ return {
27
+ pool: { next: jest.fn().mockReturnValue(mockConn) },
28
+ conn: mockConn,
29
+ pipeline: mockPipeline,
30
+ };
31
+ }
32
+ describe('StreamProducer', () => {
33
+ describe('enqueue()', () => {
34
+ it('powinien dodać komendę do strumienia przez XADD', () => __awaiter(void 0, void 0, void 0, function* () {
35
+ const { pool, conn } = createMockPool();
36
+ const producer = new stream_producer_1.StreamProducer(pool);
37
+ const data = Buffer.from('test-data');
38
+ const messageId = yield producer.enqueue('cmd:TestCommand', data);
39
+ expect(conn.xadd).toHaveBeenCalledWith('cmd:TestCommand', '*', 'data', redis_codec_1.RedisCodec.encode(data));
40
+ expect(messageId).toBe('1700000000000-0');
41
+ }));
42
+ it('powinien używać połączenia z puli', () => __awaiter(void 0, void 0, void 0, function* () {
43
+ const { pool } = createMockPool();
44
+ const producer = new stream_producer_1.StreamProducer(pool);
45
+ yield producer.enqueue('cmd:Test', Buffer.from('data'));
46
+ expect(pool.next).toHaveBeenCalledTimes(1);
47
+ }));
48
+ });
49
+ describe('enqueueBatch()', () => {
50
+ it('powinien zwrócić pustą tablicę dla pustego wejścia', () => __awaiter(void 0, void 0, void 0, function* () {
51
+ const { pool } = createMockPool();
52
+ const producer = new stream_producer_1.StreamProducer(pool);
53
+ const ids = yield producer.enqueueBatch([]);
54
+ expect(ids).toEqual([]);
55
+ }));
56
+ it('powinien zwrócić wszystkie IDs gdy brak błędów', () => __awaiter(void 0, void 0, void 0, function* () {
57
+ const { pool, pipeline } = createMockPool();
58
+ pipeline.exec.mockResolvedValue([
59
+ [null, '1700000000000-0'],
60
+ [null, '1700000000001-0'],
61
+ [null, '1700000000002-0'],
62
+ ]);
63
+ const producer = new stream_producer_1.StreamProducer(pool);
64
+ const entries = [
65
+ { streamName: 'cmd:A', data: Buffer.from('a') },
66
+ { streamName: 'cmd:B', data: Buffer.from('b') },
67
+ { streamName: 'cmd:C', data: Buffer.from('c') },
68
+ ];
69
+ const ids = yield producer.enqueueBatch(entries);
70
+ expect(ids).toEqual(['1700000000000-0', '1700000000001-0', '1700000000002-0']);
71
+ }));
72
+ it('powinien użyć pipeline dla wielu komend', () => __awaiter(void 0, void 0, void 0, function* () {
73
+ const { pool, pipeline, conn } = createMockPool();
74
+ pipeline.exec.mockResolvedValue([
75
+ [null, '1700000000000-0'],
76
+ [null, '1700000000001-0'],
77
+ ]);
78
+ const producer = new stream_producer_1.StreamProducer(pool);
79
+ yield producer.enqueueBatch([
80
+ { streamName: 'cmd:A', data: Buffer.from('a') },
81
+ { streamName: 'cmd:B', data: Buffer.from('b') },
82
+ ]);
83
+ expect(conn.pipeline).toHaveBeenCalledTimes(1);
84
+ expect(pipeline.xadd).toHaveBeenCalledTimes(2);
85
+ expect(pipeline.exec).toHaveBeenCalledTimes(1);
86
+ }));
87
+ it('powinien rzucić błąd gdy pipeline zawiera errory (H3)', () => __awaiter(void 0, void 0, void 0, function* () {
88
+ const { pool, pipeline } = createMockPool();
89
+ pipeline.exec.mockResolvedValue([
90
+ [null, '1700000000000-0'],
91
+ [new Error('WRONGTYPE'), null],
92
+ [null, '1700000000002-0'],
93
+ ]);
94
+ const producer = new stream_producer_1.StreamProducer(pool);
95
+ const entries = [
96
+ { streamName: 'cmd:A', data: Buffer.from('a') },
97
+ { streamName: 'cmd:B', data: Buffer.from('b') },
98
+ { streamName: 'cmd:C', data: Buffer.from('c') },
99
+ ];
100
+ yield expect(producer.enqueueBatch(entries)).rejects.toThrow('Błąd pipeline XADD — 1/3 operacji nie powiodło się');
101
+ }));
102
+ it('powinien rzucić błąd z indeksami failujących operacji', () => __awaiter(void 0, void 0, void 0, function* () {
103
+ const { pool, pipeline } = createMockPool();
104
+ pipeline.exec.mockResolvedValue([
105
+ [new Error('ERR1'), null],
106
+ [null, '1700000000001-0'],
107
+ [new Error('ERR2'), null],
108
+ ]);
109
+ const producer = new stream_producer_1.StreamProducer(pool);
110
+ yield expect(producer.enqueueBatch([
111
+ { streamName: 'cmd:A', data: Buffer.from('a') },
112
+ { streamName: 'cmd:B', data: Buffer.from('b') },
113
+ { streamName: 'cmd:C', data: Buffer.from('c') },
114
+ ])).rejects.toThrow('indeksy: 0, 2');
115
+ }));
116
+ it('powinien zwrócić pustą tablicę gdy exec zwraca null', () => __awaiter(void 0, void 0, void 0, function* () {
117
+ const { pool, pipeline } = createMockPool();
118
+ pipeline.exec.mockResolvedValue(null);
119
+ const producer = new stream_producer_1.StreamProducer(pool);
120
+ const ids = yield producer.enqueueBatch([{ streamName: 'cmd:A', data: Buffer.from('a') }]);
121
+ expect(ids).toEqual([]);
122
+ }));
123
+ });
124
+ });
125
+ //# sourceMappingURL=stream-producer.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-producer.spec.js","sourceRoot":"","sources":["../../../src/command-bus/transport/stream-producer.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Handler konsumenta strumienia Redis
3
+ * Otrzymuje surowe dane (Buffer) i zwraca wynik przetwarzania
4
+ *
5
+ * @param data - Surowe dane wiadomości (Buffer)
6
+ * @returns Wynik przetwarzania (lub void dla fire-and-forget)
7
+ */
8
+ export type ConsumerHandler = (data: Buffer) => Promise<unknown>;
9
+ /**
10
+ * Metadane RPC dołączane do wiadomości w strumieniu
11
+ * Umożliwiają powiązanie odpowiedzi z żądaniem
12
+ */
13
+ export interface RpcMessageMetadata {
14
+ /** Unikalny identyfikator korelacji (UUID v4) */
15
+ correlationId: string;
16
+ /** Klucz Redis listy na którą worker wyśle odpowiedź (LPUSH) */
17
+ responseKey: string;
18
+ }
19
+ /**
20
+ * Producent strumienia — dodaje wiadomości do Redis Streams
21
+ */
22
+ export interface IStreamProducer {
23
+ /**
24
+ * Dodaje komendę do strumienia (fire-and-forget)
25
+ * @param streamName - Nazwa strumienia Redis
26
+ * @param data - Surowe dane do wysłania
27
+ * @returns ID wiadomości w strumieniu
28
+ */
29
+ enqueue(streamName: string, data: Buffer): Promise<string>;
30
+ /**
31
+ * Dodaje wiele komend naraz z użyciem pipelining
32
+ * @param entries - Tablica wpisów do dodania
33
+ * @returns Tablica ID wiadomości w strumieniach
34
+ */
35
+ enqueueBatch(entries: Array<{
36
+ streamName: string;
37
+ data: Buffer;
38
+ }>): Promise<string[]>;
39
+ }
40
+ /**
41
+ * Konsument strumienia — czyta wiadomości z Redis Streams via consumer groups
42
+ */
43
+ export interface IStreamConsumer {
44
+ /**
45
+ * Rejestruje konsumenta dla strumienia z consumer group
46
+ * @param streamName - Nazwa strumienia Redis
47
+ * @param groupName - Nazwa grupy konsumentów
48
+ * @param handler - Handler przetwarzający wiadomości
49
+ */
50
+ consume(streamName: string, groupName: string, handler: ConsumerHandler): Promise<void>;
51
+ }
52
+ /**
53
+ * Transport RPC — request/response przez Redis Streams + LPUSH/BRPOP
54
+ */
55
+ export interface IRpcTransport {
56
+ /**
57
+ * RPC: wysyła request do strumienia i czeka na odpowiedź via BRPOP
58
+ * @param commandName - Nazwa komendy (używana jako nazwa strumienia)
59
+ * @param data - Surowe dane żądania
60
+ * @param timeout - Timeout w milisekundach
61
+ * @returns Buffer z odpowiedzią
62
+ */
63
+ rpcCall(commandName: string, data: Buffer, timeout: number): Promise<Buffer>;
64
+ /**
65
+ * RPC: odpowiada na request przez LPUSH + EXPIRE
66
+ * @param responseKey - Klucz Redis listy na którą wysłać odpowiedź
67
+ * @param data - Surowe dane odpowiedzi
68
+ * @param ttl - Czas życia klucza w sekundach (safety net)
69
+ */
70
+ rpcRespond(responseKey: string, data: Buffer, ttl: number): Promise<void>;
71
+ }
72
+ /**
73
+ * Zarządzanie cyklem życia — graceful shutdown
74
+ */
75
+ export interface IClosable {
76
+ /** Zamyka zasób i wszystkie połączenia */
77
+ close(): Promise<void>;
78
+ }
79
+ /**
80
+ * Pełny interfejs transportu dla CommandBus
81
+ * Kompozycja segregowanych interfejsów (ISP)
82
+ *
83
+ * Odpowiada wyłącznie za przesyłanie surowych danych (Buffer)
84
+ * Serializacja/deserializacja jest odpowiedzialnością wyższej warstwy
85
+ */
86
+ export interface ITransport extends IStreamProducer, IStreamConsumer, IRpcTransport, IClosable {
87
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=transport.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.interface.js","sourceRoot":"","sources":["../../../src/command-bus/transport/transport.interface.ts"],"names":[],"mappings":""}