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,87 @@
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.RpcHandler = void 0;
13
+ const crypto_1 = require("crypto");
14
+ const redis_codec_1 = require("./redis-codec");
15
+ /**
16
+ * Handler RPC — odpowiada za request/response przez Redis Streams + LPUSH/BRPOP
17
+ * Jedna odpowiedzialność: RPC call i respond
18
+ */
19
+ class RpcHandler {
20
+ constructor(pool, rpcPool, serializer) {
21
+ this.pool = pool;
22
+ this.rpcPool = rpcPool;
23
+ this.serializer = serializer;
24
+ }
25
+ /**
26
+ * RPC: wysyła request do strumienia i czeka na odpowiedź via BRPOP
27
+ *
28
+ * Flow (4 komendy Redis):
29
+ * 1. XADD — komenda z metadanymi RPC do strumienia
30
+ * 2. BRPOP — czekaj na odpowiedź na dedykowanym połączeniu
31
+ * 3. Worker: LPUSH — odpowiedź na klucz
32
+ * 4. DEL — cleanup klucza (TTL safety net)
33
+ */
34
+ rpcCall(commandName, data, timeout) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ // M1: Walidacja timeout — zapobiega infinite BRPOP przy 0/negatywnych/Infinity
37
+ if (!Number.isFinite(timeout) || timeout <= 0) {
38
+ throw new Error(`Nieprawidłowy timeout RPC: ${timeout}ms`);
39
+ }
40
+ const correlationId = (0, crypto_1.randomUUID)();
41
+ const responseKey = `rpc:res:${correlationId}`;
42
+ const envelope = {
43
+ commandData: redis_codec_1.RedisCodec.encode(data),
44
+ rpc: { correlationId, responseKey },
45
+ };
46
+ const envelopeBuffer = this.serializer.serialize(envelope);
47
+ // Wyślij do strumienia z markerem 'rpc' do identyfikacji
48
+ const poolConn = this.pool.next();
49
+ yield poolConn.xadd(`cmd:${commandName}`, '*', 'data', redis_codec_1.RedisCodec.encode(envelopeBuffer), 'rpc', '1');
50
+ // Pobierz dedykowane połączenie dla BRPOP (blocking)
51
+ const rpcConn = yield this.rpcPool.acquire();
52
+ try {
53
+ const timeoutSec = Math.max(1, Math.ceil(timeout / 1000));
54
+ const result = yield rpcConn.brpop(responseKey, timeoutSec);
55
+ if (!result) {
56
+ // C2: await DEL z fallback EXPIRE — zapobiega orphaned Redis keys
57
+ yield poolConn.del(responseKey).catch(() => {
58
+ // Fallback: EXPIRE zapewnia automatyczne usunięcie klucza po 120s
59
+ void poolConn.expire(responseKey, 120).catch(() => undefined);
60
+ });
61
+ // M2: bez correlationId w error message — zapobiega information leakage
62
+ throw new Error(`RPC timeout dla komendy ${commandName} (${timeout}ms)`);
63
+ }
64
+ const value = result[1];
65
+ return redis_codec_1.RedisCodec.decode(value);
66
+ }
67
+ finally {
68
+ this.rpcPool.release(rpcConn);
69
+ }
70
+ });
71
+ }
72
+ /**
73
+ * RPC: odpowiada na request przez LPUSH + EXPIRE w jednym pipeline
74
+ * Wywoływane przez worker po przetworzeniu komendy RPC
75
+ */
76
+ rpcRespond(responseKey, data, ttl) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ const conn = this.pool.next();
79
+ const pipeline = conn.pipeline();
80
+ pipeline.lpush(responseKey, redis_codec_1.RedisCodec.encode(data));
81
+ pipeline.expire(responseKey, ttl);
82
+ yield pipeline.exec();
83
+ });
84
+ }
85
+ }
86
+ exports.RpcHandler = RpcHandler;
87
+ //# sourceMappingURL=rpc-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-handler.js","sourceRoot":"","sources":["../../../src/command-bus/transport/rpc-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
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 rpc_handler_1 = require("./rpc-handler");
13
+ const redis_codec_1 = require("./redis-codec");
14
+ /**
15
+ * Mock serializera — JSON roundtrip
16
+ */
17
+ function createMockSerializer() {
18
+ return {
19
+ serialize: jest.fn((data) => Buffer.from(JSON.stringify(data))),
20
+ deserialize: jest.fn((buf) => JSON.parse(buf.toString())),
21
+ };
22
+ }
23
+ /**
24
+ * Mock połączenia Redis
25
+ */
26
+ function createMockConnection() {
27
+ return {
28
+ xadd: jest.fn().mockResolvedValue('1700000000000-0'),
29
+ del: jest.fn().mockResolvedValue(1),
30
+ expire: jest.fn().mockResolvedValue(1),
31
+ brpop: jest.fn().mockResolvedValue(null),
32
+ lpush: jest.fn().mockResolvedValue(1),
33
+ pipeline: jest.fn().mockReturnValue({
34
+ lpush: jest.fn().mockReturnThis(),
35
+ expire: jest.fn().mockReturnThis(),
36
+ exec: jest.fn().mockResolvedValue([
37
+ [null, 1],
38
+ [null, 1],
39
+ ]),
40
+ }),
41
+ };
42
+ }
43
+ function createMockPool() {
44
+ const conn = createMockConnection();
45
+ return {
46
+ pool: { next: jest.fn().mockReturnValue(conn) },
47
+ conn,
48
+ };
49
+ }
50
+ function createMockRpcPool() {
51
+ const rpcConn = createMockConnection();
52
+ return {
53
+ rpcPool: {
54
+ acquire: jest.fn().mockResolvedValue(rpcConn),
55
+ release: jest.fn(),
56
+ },
57
+ rpcConn,
58
+ };
59
+ }
60
+ describe('RpcHandler', () => {
61
+ let handler;
62
+ let mockConn;
63
+ let rpcConn;
64
+ let mockPool;
65
+ let mockRpcPool;
66
+ let serializer;
67
+ beforeEach(() => {
68
+ jest.clearAllMocks();
69
+ const poolMock = createMockPool();
70
+ const rpcPoolMock = createMockRpcPool();
71
+ mockPool = poolMock.pool;
72
+ mockConn = poolMock.conn;
73
+ mockRpcPool = rpcPoolMock.rpcPool;
74
+ rpcConn = rpcPoolMock.rpcConn;
75
+ serializer = createMockSerializer();
76
+ handler = new rpc_handler_1.RpcHandler(mockPool, mockRpcPool, serializer);
77
+ });
78
+ describe('rpcCall()', () => {
79
+ it('powinien wysłać komendę do strumienia i czekać na odpowiedź', () => __awaiter(void 0, void 0, void 0, function* () {
80
+ const responseData = Buffer.from(JSON.stringify({ result: 42 }));
81
+ rpcConn.brpop.mockResolvedValue(['rpc:res:test', redis_codec_1.RedisCodec.encode(responseData)]);
82
+ const result = yield handler.rpcCall('TestCommand', Buffer.from('request'), 5000);
83
+ // Powinien wysłać XADD z markerem rpc
84
+ expect(mockConn.xadd).toHaveBeenCalledWith('cmd:TestCommand', '*', 'data', expect.any(String), 'rpc', '1');
85
+ // Powinien pobrać i zwolnić połączenie RPC
86
+ expect(mockRpcPool.acquire).toHaveBeenCalled();
87
+ expect(mockRpcPool.release).toHaveBeenCalled();
88
+ expect(result).toBeInstanceOf(Buffer);
89
+ }));
90
+ it('powinien rzucić timeout gdy BRPOP zwraca null', () => __awaiter(void 0, void 0, void 0, function* () {
91
+ rpcConn.brpop.mockResolvedValue(null);
92
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), 1000)).rejects.toThrow('RPC timeout dla komendy TestCommand (1000ms)');
93
+ }));
94
+ it('powinien zwolnić połączenie RPC nawet przy timeout', () => __awaiter(void 0, void 0, void 0, function* () {
95
+ rpcConn.brpop.mockResolvedValue(null);
96
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), 1000)).rejects.toThrow();
97
+ expect(mockRpcPool.release).toHaveBeenCalledWith(rpcConn);
98
+ }));
99
+ it('powinien await DEL na timeout (C2)', () => __awaiter(void 0, void 0, void 0, function* () {
100
+ rpcConn.brpop.mockResolvedValue(null);
101
+ mockConn.del.mockResolvedValue(1);
102
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), 1000)).rejects.toThrow();
103
+ expect(mockConn.del).toHaveBeenCalled();
104
+ }));
105
+ it('powinien ustawić EXPIRE jako fallback gdy DEL failuje (C2)', () => __awaiter(void 0, void 0, void 0, function* () {
106
+ rpcConn.brpop.mockResolvedValue(null);
107
+ mockConn.del.mockRejectedValue(new Error('Connection lost'));
108
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), 1000)).rejects.toThrow('RPC timeout');
109
+ expect(mockConn.del).toHaveBeenCalled();
110
+ expect(mockConn.expire).toHaveBeenCalledWith(expect.stringContaining('rpc:res:'), 120);
111
+ }));
112
+ it('nie powinien rzucać gdy zarówno DEL jak i EXPIRE failują (C2)', () => __awaiter(void 0, void 0, void 0, function* () {
113
+ rpcConn.brpop.mockResolvedValue(null);
114
+ mockConn.del.mockRejectedValue(new Error('Connection lost'));
115
+ mockConn.expire.mockRejectedValue(new Error('Connection lost'));
116
+ // Powinien rzucić TYLKO RPC timeout, nie błąd z DEL/EXPIRE
117
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), 1000)).rejects.toThrow('RPC timeout');
118
+ }));
119
+ it('nie powinien zawierać correlationId w error message (M2)', () => __awaiter(void 0, void 0, void 0, function* () {
120
+ rpcConn.brpop.mockResolvedValue(null);
121
+ try {
122
+ yield handler.rpcCall('TestCommand', Buffer.from('data'), 1000);
123
+ }
124
+ catch (error) {
125
+ const message = error.message;
126
+ expect(message).not.toContain('correlationId');
127
+ }
128
+ }));
129
+ it('powinien rzucić błąd przy timeout 0 (M1)', () => __awaiter(void 0, void 0, void 0, function* () {
130
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), 0)).rejects.toThrow('Nieprawidłowy timeout RPC: 0ms');
131
+ }));
132
+ it('powinien rzucić błąd przy negatywnym timeout (M1)', () => __awaiter(void 0, void 0, void 0, function* () {
133
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), -100)).rejects.toThrow('Nieprawidłowy timeout RPC: -100ms');
134
+ }));
135
+ it('powinien rzucić błąd przy Infinity timeout (M1)', () => __awaiter(void 0, void 0, void 0, function* () {
136
+ yield expect(handler.rpcCall('TestCommand', Buffer.from('data'), Infinity)).rejects.toThrow('Nieprawidłowy timeout RPC: Infinityms');
137
+ }));
138
+ it('powinien użyć minimum 1 sekundy dla BRPOP timeout', () => __awaiter(void 0, void 0, void 0, function* () {
139
+ const responseData = Buffer.from(JSON.stringify({ result: 'ok' }));
140
+ rpcConn.brpop.mockResolvedValue(['rpc:res:test', redis_codec_1.RedisCodec.encode(responseData)]);
141
+ yield handler.rpcCall('TestCommand', Buffer.from('data'), 100); // 100ms < 1s
142
+ // BRPOP powinien mieć timeout = 1 (minimalna wartość)
143
+ expect(rpcConn.brpop).toHaveBeenCalledWith(expect.stringContaining('rpc:res:'), 1);
144
+ }));
145
+ });
146
+ describe('rpcRespond()', () => {
147
+ it('powinien wysłać odpowiedź przez pipeline LPUSH + EXPIRE', () => __awaiter(void 0, void 0, void 0, function* () {
148
+ const responseData = Buffer.from('response');
149
+ yield handler.rpcRespond('rpc:res:test-id', responseData, 60);
150
+ const pipeline = mockConn.pipeline();
151
+ expect(mockConn.pipeline).toHaveBeenCalled();
152
+ expect(pipeline.lpush).toHaveBeenCalledWith('rpc:res:test-id', redis_codec_1.RedisCodec.encode(responseData));
153
+ expect(pipeline.expire).toHaveBeenCalledWith('rpc:res:test-id', 60);
154
+ }));
155
+ });
156
+ });
157
+ //# sourceMappingURL=rpc-handler.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rpc-handler.spec.js","sourceRoot":"","sources":["../../../src/command-bus/transport/rpc-handler.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,89 @@
1
+ import type { ILogger } from '../../shared/types';
2
+ import type { ISerializer } from '../serialization/serializer.interface';
3
+ import type { IStreamConsumer, ConsumerHandler } from './transport.interface';
4
+ import type RedisConnectionPool from '../../shared/redis/connection-pool';
5
+ import type { RpcResponder } from './message-processor';
6
+ /**
7
+ * Konfiguracja StreamConsumer
8
+ */
9
+ export interface StreamConsumerOptions {
10
+ pool: RedisConnectionPool;
11
+ serializer: ISerializer;
12
+ logger: ILogger;
13
+ consumerId: string;
14
+ batchSize: number;
15
+ concurrency: number;
16
+ maxRetained: number;
17
+ rpcRespond: RpcResponder;
18
+ /** Próg czasu (ms) po którym wpis w processing Map jest uznawany za stale (domyślnie 300_000 = 5min) */
19
+ staleThreshold?: number;
20
+ /** Minimalny interwał (ms) między sweepami stale wpisów (domyślnie 10_000 = 10s) */
21
+ sweepInterval?: number;
22
+ }
23
+ /**
24
+ * Koordynator konsumenta strumienia Redis — komponuje MessageProcessor i ConsumerLoop
25
+ *
26
+ * Odpowiedzialności:
27
+ * - Zarządzanie cyklem życia: tworzenie połączeń, consumer groups, uruchamianie loops
28
+ * - Deduplicacja (processing Map z TTL sweep) (H1)
29
+ * - Koordynacja komponentów: MessageProcessor i ConsumerLoop
30
+ *
31
+ * NIE odpowiada za:
32
+ * - Przetwarzanie wiadomości (MessageProcessor)
33
+ * - Consumer loop z concurrency limiter (ConsumerLoop)
34
+ */
35
+ export declare class StreamConsumer implements IStreamConsumer {
36
+ private readonly pool;
37
+ private readonly logger;
38
+ private readonly consumerId;
39
+ private readonly batchSize;
40
+ private readonly concurrency;
41
+ /** H1: Próg czasu (ms) po którym wpis w processing Map jest uznawany za stale */
42
+ private readonly staleThreshold;
43
+ /** Timestamp ostatniego sweep — throttle co sweepInterval */
44
+ private lastSweepTime;
45
+ /** Minimalny interwał między sweep (ms) */
46
+ private readonly sweepInterval;
47
+ /** Procesor wiadomości — parsuje, waliduje, przetwarza */
48
+ private readonly messageProcessor;
49
+ /** Instancje consumer loops — do propagacji flagi running */
50
+ private readonly loops;
51
+ /** Dedykowane połączenia konsumentów (XREADGROUP BLOCK) */
52
+ private readonly consumerConnections;
53
+ /** Promises consumer loops — do graceful shutdown */
54
+ private readonly consumerLoopPromises;
55
+ /** H1: Wiadomości aktualnie przetwarzane — Map<messageId, timestamp> z TTL sweep */
56
+ private readonly processing;
57
+ /** Flaga zatrzymania — propaguje do wszystkich consumer loops */
58
+ private _running;
59
+ get running(): boolean;
60
+ set running(value: boolean);
61
+ /** Zwraca liczbę aktywnych konsumentów (do logów) */
62
+ get consumerCount(): number;
63
+ /**
64
+ * Zamyka dedykowane połączenia konsumentów (przerywa XREADGROUP BLOCK)
65
+ */
66
+ closeConnections(): void;
67
+ /**
68
+ * Czeka na zakończenie consumer loops
69
+ */
70
+ awaitLoops(): Promise<void>;
71
+ constructor(options: StreamConsumerOptions);
72
+ /**
73
+ * Rejestruje konsumenta dla strumienia z consumer group
74
+ * Tworzy dedykowane połączenie (XREADGROUP BLOCK blokuje socket)
75
+ */
76
+ consume(streamName: string, groupName: string, handler: ConsumerHandler): Promise<void>;
77
+ /**
78
+ * Przetwarza pojedynczą wiadomość ze strumienia
79
+ * Deduplicacja + delegacja do MessageProcessor
80
+ *
81
+ * Wywoływane zarówno przez ConsumerLoop (nowe wiadomości) jak i PendingRecovery (XCLAIM)
82
+ */
83
+ processMessage(streamName: string, groupName: string, messageId: string, fields: string[], handler: ConsumerHandler): Promise<void>;
84
+ /**
85
+ * H1: Usuwa stale wpisy z processing Map — zapobiega permanentnej blokadzie recovery
86
+ * Wpis uznawany za stale gdy trwa dłużej niż staleThreshold
87
+ */
88
+ private sweepStaleProcessing;
89
+ }
@@ -0,0 +1,181 @@
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.StreamConsumer = void 0;
13
+ const message_processor_1 = require("./message-processor");
14
+ const consumer_loop_1 = require("./consumer-loop");
15
+ const error_utils_1 = require("../../shared/utils/error-utils");
16
+ /**
17
+ * Koordynator konsumenta strumienia Redis — komponuje MessageProcessor i ConsumerLoop
18
+ *
19
+ * Odpowiedzialności:
20
+ * - Zarządzanie cyklem życia: tworzenie połączeń, consumer groups, uruchamianie loops
21
+ * - Deduplicacja (processing Map z TTL sweep) (H1)
22
+ * - Koordynacja komponentów: MessageProcessor i ConsumerLoop
23
+ *
24
+ * NIE odpowiada za:
25
+ * - Przetwarzanie wiadomości (MessageProcessor)
26
+ * - Consumer loop z concurrency limiter (ConsumerLoop)
27
+ */
28
+ class StreamConsumer {
29
+ get running() {
30
+ return this._running;
31
+ }
32
+ set running(value) {
33
+ this._running = value;
34
+ for (const loop of this.loops) {
35
+ loop.running = value;
36
+ }
37
+ }
38
+ /** Zwraca liczbę aktywnych konsumentów (do logów) */
39
+ get consumerCount() {
40
+ return this.consumerConnections.length;
41
+ }
42
+ /**
43
+ * Zamyka dedykowane połączenia konsumentów (przerywa XREADGROUP BLOCK)
44
+ */
45
+ closeConnections() {
46
+ for (const conn of this.consumerConnections) {
47
+ conn.disconnect();
48
+ }
49
+ this.consumerConnections.length = 0;
50
+ }
51
+ /**
52
+ * Czeka na zakończenie consumer loops
53
+ */
54
+ awaitLoops() {
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ if (this.consumerLoopPromises.length > 0) {
57
+ yield Promise.allSettled(this.consumerLoopPromises);
58
+ this.consumerLoopPromises.length = 0;
59
+ }
60
+ });
61
+ }
62
+ constructor(options) {
63
+ var _a, _b;
64
+ /** Timestamp ostatniego sweep — throttle co sweepInterval */
65
+ this.lastSweepTime = 0;
66
+ /** Instancje consumer loops — do propagacji flagi running */
67
+ this.loops = [];
68
+ /** Dedykowane połączenia konsumentów (XREADGROUP BLOCK) */
69
+ this.consumerConnections = [];
70
+ /** Promises consumer loops — do graceful shutdown */
71
+ this.consumerLoopPromises = [];
72
+ /** H1: Wiadomości aktualnie przetwarzane — Map<messageId, timestamp> z TTL sweep */
73
+ this.processing = new Map();
74
+ /** Flaga zatrzymania — propaguje do wszystkich consumer loops */
75
+ this._running = true;
76
+ this.pool = options.pool;
77
+ this.logger = options.logger;
78
+ this.consumerId = options.consumerId;
79
+ this.batchSize = options.batchSize;
80
+ this.concurrency = options.concurrency;
81
+ this.staleThreshold = (_a = options.staleThreshold) !== null && _a !== void 0 ? _a : 300000;
82
+ this.sweepInterval = (_b = options.sweepInterval) !== null && _b !== void 0 ? _b : 10000;
83
+ this.messageProcessor = new message_processor_1.MessageProcessor({
84
+ pool: options.pool,
85
+ serializer: options.serializer,
86
+ logger: options.logger,
87
+ batchSize: options.batchSize,
88
+ maxRetained: options.maxRetained,
89
+ rpcRespond: options.rpcRespond,
90
+ });
91
+ }
92
+ /**
93
+ * Rejestruje konsumenta dla strumienia z consumer group
94
+ * Tworzy dedykowane połączenie (XREADGROUP BLOCK blokuje socket)
95
+ */
96
+ consume(streamName, groupName, handler) {
97
+ return __awaiter(this, void 0, void 0, function* () {
98
+ const conn = this.pool.createDedicated(streamName);
99
+ this.consumerConnections.push(conn);
100
+ // Utwórz consumer group (ignoruj jeśli już istnieje)
101
+ try {
102
+ yield conn.xgroup('CREATE', streamName, groupName, '0', 'MKSTREAM');
103
+ }
104
+ catch (error) {
105
+ const msg = (0, error_utils_1.getErrorMessage)(error);
106
+ if (!msg.includes('BUSYGROUP')) {
107
+ throw error;
108
+ }
109
+ }
110
+ // Utwórz consumer loop i uruchom w tle
111
+ const loop = new consumer_loop_1.ConsumerLoop({
112
+ consumerId: this.consumerId,
113
+ batchSize: this.batchSize,
114
+ concurrency: this.concurrency,
115
+ logger: this.logger,
116
+ });
117
+ this.loops.push(loop);
118
+ const loopPromise = loop.run(conn, streamName, groupName, (messageId, fields) => this.processMessage(streamName, groupName, messageId, fields, handler));
119
+ this.consumerLoopPromises.push(loopPromise);
120
+ this.logger.debug('Konsument zarejestrowany', {
121
+ streamName,
122
+ groupName,
123
+ consumerId: this.consumerId,
124
+ timestamp: new Date().toISOString(),
125
+ });
126
+ });
127
+ }
128
+ /**
129
+ * Przetwarza pojedynczą wiadomość ze strumienia
130
+ * Deduplicacja + delegacja do MessageProcessor
131
+ *
132
+ * Wywoływane zarówno przez ConsumerLoop (nowe wiadomości) jak i PendingRecovery (XCLAIM)
133
+ */
134
+ processMessage(streamName, groupName, messageId, fields, handler) {
135
+ return __awaiter(this, void 0, void 0, function* () {
136
+ // H1: Throttled sweep stale wpisów — co sweepInterval zamiast na każdą wiadomość
137
+ const now = Date.now();
138
+ if (now - this.lastSweepTime > this.sweepInterval) {
139
+ this.sweepStaleProcessing();
140
+ this.lastSweepTime = now;
141
+ }
142
+ if (this.processing.has(messageId)) {
143
+ return;
144
+ }
145
+ this.processing.set(messageId, Date.now());
146
+ try {
147
+ yield this.messageProcessor.process(streamName, groupName, messageId, fields, handler);
148
+ }
149
+ catch (error) {
150
+ this.logger.error('Błąd przetwarzania wiadomości', {
151
+ streamName,
152
+ messageId,
153
+ error: (0, error_utils_1.getErrorMessage)(error),
154
+ timestamp: new Date().toISOString(),
155
+ });
156
+ }
157
+ finally {
158
+ this.processing.delete(messageId);
159
+ }
160
+ });
161
+ }
162
+ /**
163
+ * H1: Usuwa stale wpisy z processing Map — zapobiega permanentnej blokadzie recovery
164
+ * Wpis uznawany za stale gdy trwa dłużej niż staleThreshold
165
+ */
166
+ sweepStaleProcessing() {
167
+ const now = Date.now();
168
+ for (const [id, timestamp] of this.processing) {
169
+ if (now - timestamp > this.staleThreshold) {
170
+ this.processing.delete(id);
171
+ this.logger.warn('Usunięto stale wpis z processing Map', {
172
+ messageId: id,
173
+ age: now - timestamp,
174
+ threshold: this.staleThreshold,
175
+ });
176
+ }
177
+ }
178
+ }
179
+ }
180
+ exports.StreamConsumer = StreamConsumer;
181
+ //# sourceMappingURL=stream-consumer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-consumer.js","sourceRoot":"","sources":["../../../src/command-bus/transport/stream-consumer.ts"],"names":[],"mappings":";;;;;;;;;;;;AAKA,2DAAuD;AAEvD,mDAA+C;AAC/C,gEAAiE;AAoBjE;;;;;;;;;;;GAWG;AACH,MAAa,cAAc;IA4BzB,IAAI,OAAO;QACT,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,IAAI,OAAO,CAAC,KAAc;QACxB,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACtB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;QACvB,CAAC;IACH,CAAC;IAED,qDAAqD;IACrD,IAAI,aAAa;QACf,OAAO,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC;IACzC,CAAC;IAED;;OAEG;IACI,gBAAgB;QACrB,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC5C,IAAI,CAAC,UAAU,EAAE,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,MAAM,GAAG,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACU,UAAU;;YACrB,IAAI,IAAI,CAAC,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzC,MAAM,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;gBACpD,IAAI,CAAC,oBAAoB,CAAC,MAAM,GAAG,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;KAAA;IAED,YAAY,OAA8B;;QAxD1C,6DAA6D;QACrD,kBAAa,GAAG,CAAC,CAAC;QAM1B,6DAA6D;QAC5C,UAAK,GAAmB,EAAE,CAAC;QAE5C,2DAA2D;QAC1C,wBAAmB,GAAY,EAAE,CAAC;QACnD,qDAAqD;QACpC,yBAAoB,GAAoB,EAAE,CAAC;QAC5D,oFAAoF;QACnE,eAAU,GAAG,IAAI,GAAG,EAAkB,CAAC;QAExD,iEAAiE;QACzD,aAAQ,GAAG,IAAI,CAAC;QAuCtB,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QACrC,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;QACnC,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;QACvC,IAAI,CAAC,cAAc,GAAG,MAAA,OAAO,CAAC,cAAc,mCAAI,MAAO,CAAC;QACxD,IAAI,CAAC,aAAa,GAAG,MAAA,OAAO,CAAC,aAAa,mCAAI,KAAM,CAAC;QAErD,IAAI,CAAC,gBAAgB,GAAG,IAAI,oCAAgB,CAAC;YAC3C,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,UAAU,EAAE,OAAO,CAAC,UAAU;SAC/B,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACU,OAAO,CAClB,UAAkB,EAClB,SAAiB,EACjB,OAAwB;;YAExB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC;YACnD,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEpC,qDAAqD;YACrD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC;YACtE,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,GAAG,GAAG,IAAA,6BAAe,EAAC,KAAK,CAAC,CAAC;gBACnC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;oBAC/B,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;YAED,uCAAuC;YACvC,MAAM,IAAI,GAAG,IAAI,4BAAY,CAAC;gBAC5B,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,SAAS,EAAE,IAAI,CAAC,SAAS;gBACzB,WAAW,EAAE,IAAI,CAAC,WAAW;gBAC7B,MAAM,EAAE,IAAI,CAAC,MAAM;aACpB,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAEtB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,CAC9E,IAAI,CAAC,cAAc,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CACvE,CAAC;YACF,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;YAE5C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE;gBAC5C,UAAU;gBACV,SAAS;gBACT,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC,CAAC,CAAC;QACL,CAAC;KAAA;IAED;;;;;OAKG;IACG,cAAc,CAClB,UAAkB,EAClB,SAAiB,EACjB,SAAiB,EACjB,MAAgB,EAChB,OAAwB;;YAExB,iFAAiF;YACjF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YACvB,IAAI,GAAG,GAAG,IAAI,CAAC,aAAa,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;gBAClD,IAAI,CAAC,oBAAoB,EAAE,CAAC;gBAC5B,IAAI,CAAC,aAAa,GAAG,GAAG,CAAC;YAC3B,CAAC;YAED,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;gBACnC,OAAO;YACT,CAAC;YACD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;YAE3C,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,UAAU,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;YACzF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE;oBACjD,UAAU;oBACV,SAAS;oBACT,KAAK,EAAE,IAAA,6BAAe,EAAC,KAAK,CAAC;oBAC7B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;iBACpC,CAAC,CAAC;YACL,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;YACpC,CAAC;QACH,CAAC;KAAA;IAED;;;OAGG;IACK,oBAAoB;QAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,EAAE,EAAE,SAAS,CAAC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YAC9C,IAAI,GAAG,GAAG,SAAS,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;gBAC1C,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBAC3B,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,sCAAsC,EAAE;oBACvD,SAAS,EAAE,EAAE;oBACb,GAAG,EAAE,GAAG,GAAG,SAAS;oBACpB,SAAS,EAAE,IAAI,CAAC,cAAc;iBAC/B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;CACF;AAvLD,wCAuLC"}