bunqueue 1.9.7 → 1.9.8

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 (145) hide show
  1. package/dist/application/backgroundTasks.d.ts +3 -6
  2. package/dist/application/backgroundTasks.d.ts.map +1 -1
  3. package/dist/application/backgroundTasks.js +9 -172
  4. package/dist/application/backgroundTasks.js.map +1 -1
  5. package/dist/application/cleanupTasks.d.ts +1 -1
  6. package/dist/application/cleanupTasks.d.ts.map +1 -1
  7. package/dist/application/cleanupTasks.js +57 -22
  8. package/dist/application/cleanupTasks.js.map +1 -1
  9. package/dist/application/clientTracking.d.ts +22 -0
  10. package/dist/application/clientTracking.d.ts.map +1 -0
  11. package/dist/application/clientTracking.js +122 -0
  12. package/dist/application/clientTracking.js.map +1 -0
  13. package/dist/application/contextFactory.d.ts +97 -0
  14. package/dist/application/contextFactory.d.ts.map +1 -0
  15. package/dist/application/contextFactory.js +169 -0
  16. package/dist/application/contextFactory.js.map +1 -0
  17. package/dist/application/dependencyProcessor.d.ts +11 -0
  18. package/dist/application/dependencyProcessor.d.ts.map +1 -0
  19. package/dist/application/dependencyProcessor.js +69 -0
  20. package/dist/application/dependencyProcessor.js.map +1 -0
  21. package/dist/application/dlqManager.d.ts +12 -0
  22. package/dist/application/dlqManager.d.ts.map +1 -1
  23. package/dist/application/dlqManager.js +36 -0
  24. package/dist/application/dlqManager.js.map +1 -1
  25. package/dist/application/lockManager.d.ts +2 -49
  26. package/dist/application/lockManager.d.ts.map +1 -1
  27. package/dist/application/lockManager.js +73 -262
  28. package/dist/application/lockManager.js.map +1 -1
  29. package/dist/application/lockOperations.d.ts +39 -0
  30. package/dist/application/lockOperations.d.ts.map +1 -0
  31. package/dist/application/lockOperations.js +101 -0
  32. package/dist/application/lockOperations.js.map +1 -0
  33. package/dist/application/operations/ack.d.ts +0 -5
  34. package/dist/application/operations/ack.d.ts.map +1 -1
  35. package/dist/application/operations/ack.js +30 -258
  36. package/dist/application/operations/ack.js.map +1 -1
  37. package/dist/application/operations/ackHelpers.d.ts +78 -0
  38. package/dist/application/operations/ackHelpers.d.ts.map +1 -0
  39. package/dist/application/operations/ackHelpers.js +162 -0
  40. package/dist/application/operations/ackHelpers.js.map +1 -0
  41. package/dist/application/operations/jobManagement.d.ts +2 -0
  42. package/dist/application/operations/jobManagement.d.ts.map +1 -1
  43. package/dist/application/operations/jobManagement.js +8 -0
  44. package/dist/application/operations/jobManagement.js.map +1 -1
  45. package/dist/application/operations/push.d.ts.map +1 -1
  46. package/dist/application/operations/push.js +8 -2
  47. package/dist/application/operations/push.js.map +1 -1
  48. package/dist/application/operations/queryOperations.d.ts +11 -0
  49. package/dist/application/operations/queryOperations.d.ts.map +1 -1
  50. package/dist/application/operations/queryOperations.js +32 -0
  51. package/dist/application/operations/queryOperations.js.map +1 -1
  52. package/dist/application/queueManager.d.ts +3 -11
  53. package/dist/application/queueManager.d.ts.map +1 -1
  54. package/dist/application/queueManager.js +90 -243
  55. package/dist/application/queueManager.js.map +1 -1
  56. package/dist/application/stallDetection.d.ts +11 -0
  57. package/dist/application/stallDetection.d.ts.map +1 -0
  58. package/dist/application/stallDetection.js +128 -0
  59. package/dist/application/stallDetection.js.map +1 -0
  60. package/dist/domain/queue/dependencyTracker.d.ts +74 -0
  61. package/dist/domain/queue/dependencyTracker.d.ts.map +1 -0
  62. package/dist/domain/queue/dependencyTracker.js +126 -0
  63. package/dist/domain/queue/dependencyTracker.js.map +1 -0
  64. package/dist/domain/queue/dlqShard.d.ts +59 -0
  65. package/dist/domain/queue/dlqShard.d.ts.map +1 -0
  66. package/dist/domain/queue/dlqShard.js +165 -0
  67. package/dist/domain/queue/dlqShard.js.map +1 -0
  68. package/dist/domain/queue/limiterManager.d.ts +44 -0
  69. package/dist/domain/queue/limiterManager.d.ts.map +1 -0
  70. package/dist/domain/queue/limiterManager.js +99 -0
  71. package/dist/domain/queue/limiterManager.js.map +1 -0
  72. package/dist/domain/queue/shard.d.ts +29 -122
  73. package/dist/domain/queue/shard.d.ts.map +1 -1
  74. package/dist/domain/queue/shard.js +152 -426
  75. package/dist/domain/queue/shard.js.map +1 -1
  76. package/dist/domain/queue/temporalManager.d.ts +81 -0
  77. package/dist/domain/queue/temporalManager.d.ts.map +1 -0
  78. package/dist/domain/queue/temporalManager.js +149 -0
  79. package/dist/domain/queue/temporalManager.js.map +1 -0
  80. package/dist/domain/queue/uniqueKeyManager.d.ts +32 -0
  81. package/dist/domain/queue/uniqueKeyManager.d.ts.map +1 -0
  82. package/dist/domain/queue/uniqueKeyManager.js +87 -0
  83. package/dist/domain/queue/uniqueKeyManager.js.map +1 -0
  84. package/dist/infrastructure/backup/s3Backup.d.ts +3 -40
  85. package/dist/infrastructure/backup/s3Backup.d.ts.map +1 -1
  86. package/dist/infrastructure/backup/s3Backup.js +10 -182
  87. package/dist/infrastructure/backup/s3Backup.js.map +1 -1
  88. package/dist/infrastructure/backup/s3BackupConfig.d.ts +67 -0
  89. package/dist/infrastructure/backup/s3BackupConfig.d.ts.map +1 -0
  90. package/dist/infrastructure/backup/s3BackupConfig.js +48 -0
  91. package/dist/infrastructure/backup/s3BackupConfig.js.map +1 -0
  92. package/dist/infrastructure/backup/s3BackupOperations.d.ts +23 -0
  93. package/dist/infrastructure/backup/s3BackupOperations.d.ts.map +1 -0
  94. package/dist/infrastructure/backup/s3BackupOperations.js +170 -0
  95. package/dist/infrastructure/backup/s3BackupOperations.js.map +1 -0
  96. package/dist/infrastructure/persistence/sqlite.d.ts +4 -13
  97. package/dist/infrastructure/persistence/sqlite.d.ts.map +1 -1
  98. package/dist/infrastructure/persistence/sqlite.js +23 -178
  99. package/dist/infrastructure/persistence/sqlite.js.map +1 -1
  100. package/dist/infrastructure/persistence/sqliteBatch.d.ts +38 -0
  101. package/dist/infrastructure/persistence/sqliteBatch.d.ts.map +1 -0
  102. package/dist/infrastructure/persistence/sqliteBatch.js +124 -0
  103. package/dist/infrastructure/persistence/sqliteBatch.js.map +1 -0
  104. package/dist/infrastructure/persistence/sqliteSerializer.d.ts +17 -0
  105. package/dist/infrastructure/persistence/sqliteSerializer.d.ts.map +1 -0
  106. package/dist/infrastructure/persistence/sqliteSerializer.js +81 -0
  107. package/dist/infrastructure/persistence/sqliteSerializer.js.map +1 -0
  108. package/dist/infrastructure/server/handler.d.ts.map +1 -1
  109. package/dist/infrastructure/server/handler.js +1 -186
  110. package/dist/infrastructure/server/handler.js.map +1 -1
  111. package/dist/infrastructure/server/handlerRoutes.d.ts +23 -0
  112. package/dist/infrastructure/server/handlerRoutes.d.ts.map +1 -0
  113. package/dist/infrastructure/server/handlerRoutes.js +190 -0
  114. package/dist/infrastructure/server/handlerRoutes.js.map +1 -0
  115. package/dist/infrastructure/server/http.d.ts +4 -25
  116. package/dist/infrastructure/server/http.d.ts.map +1 -1
  117. package/dist/infrastructure/server/http.js +43 -285
  118. package/dist/infrastructure/server/http.js.map +1 -1
  119. package/dist/infrastructure/server/httpEndpoints.d.ts +19 -0
  120. package/dist/infrastructure/server/httpEndpoints.d.ts.map +1 -0
  121. package/dist/infrastructure/server/httpEndpoints.js +151 -0
  122. package/dist/infrastructure/server/httpEndpoints.js.map +1 -0
  123. package/dist/infrastructure/server/sseHandler.d.ts +27 -0
  124. package/dist/infrastructure/server/sseHandler.d.ts.map +1 -0
  125. package/dist/infrastructure/server/sseHandler.js +77 -0
  126. package/dist/infrastructure/server/sseHandler.js.map +1 -0
  127. package/dist/infrastructure/server/wsHandler.d.ts +31 -0
  128. package/dist/infrastructure/server/wsHandler.d.ts.map +1 -0
  129. package/dist/infrastructure/server/wsHandler.js +63 -0
  130. package/dist/infrastructure/server/wsHandler.js.map +1 -0
  131. package/dist/mcp/index.js +3 -465
  132. package/dist/mcp/index.js.map +1 -1
  133. package/dist/mcp/mcpHandlers.d.ts +129 -0
  134. package/dist/mcp/mcpHandlers.d.ts.map +1 -0
  135. package/dist/mcp/mcpHandlers.js +204 -0
  136. package/dist/mcp/mcpHandlers.js.map +1 -0
  137. package/dist/mcp/mcpTools.d.ts +15 -0
  138. package/dist/mcp/mcpTools.d.ts.map +1 -0
  139. package/dist/mcp/mcpTools.js +277 -0
  140. package/dist/mcp/mcpTools.js.map +1 -0
  141. package/package.json +2 -2
  142. package/dist/cli/dashboard.d.ts +0 -32
  143. package/dist/cli/dashboard.d.ts.map +0 -1
  144. package/dist/cli/dashboard.js +0 -183
  145. package/dist/cli/dashboard.js.map +0 -1
@@ -0,0 +1,151 @@
1
+ /**
2
+ * HTTP Endpoints - Health, diagnostics, and stats endpoints
3
+ */
4
+ import { VERSION } from '../../shared/version';
5
+ /** JSON response helper */
6
+ export function jsonResponse(data, status = 200, corsOrigins) {
7
+ const headers = {
8
+ 'Content-Type': 'application/json',
9
+ };
10
+ if (corsOrigins) {
11
+ headers['Access-Control-Allow-Origin'] = corsOrigins.has('*')
12
+ ? '*'
13
+ : Array.from(corsOrigins).join(', ');
14
+ }
15
+ return new Response(JSON.stringify(data), { status, headers });
16
+ }
17
+ /** CORS preflight response */
18
+ export function corsResponse(corsOrigins) {
19
+ return new Response(null, {
20
+ status: 204,
21
+ headers: {
22
+ 'Access-Control-Allow-Origin': corsOrigins.has('*')
23
+ ? '*'
24
+ : Array.from(corsOrigins).join(', '),
25
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
26
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization',
27
+ 'Access-Control-Max-Age': '86400',
28
+ },
29
+ });
30
+ }
31
+ /** Health check endpoint */
32
+ export function healthEndpoint(queueManager, wsCount, sseCount) {
33
+ const stats = queueManager.getStats();
34
+ const uptime = process.uptime();
35
+ const memoryUsage = process.memoryUsage();
36
+ return jsonResponse({
37
+ ok: true,
38
+ status: 'healthy',
39
+ uptime: Math.floor(uptime),
40
+ version: VERSION,
41
+ queues: {
42
+ waiting: stats.waiting,
43
+ active: stats.active,
44
+ delayed: stats.delayed,
45
+ completed: stats.completed,
46
+ dlq: stats.dlq,
47
+ },
48
+ connections: {
49
+ tcp: 0,
50
+ ws: wsCount,
51
+ sse: sseCount,
52
+ },
53
+ memory: {
54
+ heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024),
55
+ heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024),
56
+ rss: Math.round(memoryUsage.rss / 1024 / 1024),
57
+ },
58
+ });
59
+ }
60
+ /** GC endpoint - force garbage collection */
61
+ export function gcEndpoint(queueManager) {
62
+ const before = process.memoryUsage();
63
+ if (typeof Bun !== 'undefined' && Bun.gc) {
64
+ Bun.gc(true);
65
+ }
66
+ queueManager.compactMemory();
67
+ const after = process.memoryUsage();
68
+ return jsonResponse({
69
+ ok: true,
70
+ before: {
71
+ heapUsed: Math.round(before.heapUsed / 1024 / 1024),
72
+ heapTotal: Math.round(before.heapTotal / 1024 / 1024),
73
+ rss: Math.round(before.rss / 1024 / 1024),
74
+ },
75
+ after: {
76
+ heapUsed: Math.round(after.heapUsed / 1024 / 1024),
77
+ heapTotal: Math.round(after.heapTotal / 1024 / 1024),
78
+ rss: Math.round(after.rss / 1024 / 1024),
79
+ },
80
+ });
81
+ }
82
+ /** Heap stats endpoint - for debugging memory leaks */
83
+ export async function heapStatsEndpoint(queueManager) {
84
+ if (typeof Bun !== 'undefined' && Bun.gc) {
85
+ Bun.gc(true);
86
+ }
87
+ const { heapStats } = await import('bun:jsc');
88
+ const stats = heapStats();
89
+ const mem = process.memoryUsage();
90
+ const memStats = queueManager.getMemoryStats();
91
+ const typeCounts = stats.objectTypeCounts;
92
+ const topTypes = typeCounts
93
+ ? Object.entries(typeCounts)
94
+ .sort((a, b) => b[1] - a[1])
95
+ .slice(0, 20)
96
+ .map(([type, count]) => ({ type, count }))
97
+ : [];
98
+ return jsonResponse({
99
+ ok: true,
100
+ memory: {
101
+ heapUsed: Math.round(mem.heapUsed / 1024 / 1024),
102
+ heapTotal: Math.round(mem.heapTotal / 1024 / 1024),
103
+ rss: Math.round(mem.rss / 1024 / 1024),
104
+ },
105
+ heap: {
106
+ objectCount: stats.objectCount,
107
+ protectedCount: stats.protectedObjectCount,
108
+ globalCount: stats.globalObjectCount,
109
+ },
110
+ collections: memStats,
111
+ topObjectTypes: topTypes,
112
+ });
113
+ }
114
+ /** Stats endpoint */
115
+ export function statsEndpoint(queueManager, corsOrigins) {
116
+ const stats = queueManager.getStats();
117
+ const memStats = queueManager.getMemoryStats();
118
+ const mem = process.memoryUsage();
119
+ return jsonResponse({
120
+ ok: true,
121
+ stats: {
122
+ ...stats,
123
+ totalPushed: Number(stats.totalPushed),
124
+ totalPulled: Number(stats.totalPulled),
125
+ totalCompleted: Number(stats.totalCompleted),
126
+ totalFailed: Number(stats.totalFailed),
127
+ },
128
+ memory: {
129
+ heapUsed: Math.round(mem.heapUsed / 1024 / 1024),
130
+ heapTotal: Math.round(mem.heapTotal / 1024 / 1024),
131
+ rss: Math.round(mem.rss / 1024 / 1024),
132
+ external: Math.round(mem.external / 1024 / 1024),
133
+ arrayBuffers: Math.round(mem.arrayBuffers / 1024 / 1024),
134
+ },
135
+ collections: memStats,
136
+ }, 200, corsOrigins);
137
+ }
138
+ /** Metrics endpoint */
139
+ export function metricsEndpoint(queueManager, corsOrigins) {
140
+ const stats = queueManager.getStats();
141
+ return jsonResponse({
142
+ ok: true,
143
+ metrics: {
144
+ totalPushed: Number(stats.totalPushed),
145
+ totalPulled: Number(stats.totalPulled),
146
+ totalCompleted: Number(stats.totalCompleted),
147
+ totalFailed: Number(stats.totalFailed),
148
+ },
149
+ }, 200, corsOrigins);
150
+ }
151
+ //# sourceMappingURL=httpEndpoints.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpEndpoints.js","sourceRoot":"","sources":["../../../src/infrastructure/server/httpEndpoints.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * SSE Handler - Server-Sent Events management
3
+ */
4
+ import type { JobEvent } from '../../domain/types/queue';
5
+ /** SSE client tracking */
6
+ export interface SseClient {
7
+ id: string;
8
+ controller: ReadableStreamDefaultController;
9
+ queueFilter: string | null;
10
+ }
11
+ /**
12
+ * SSE Handler - manages Server-Sent Events clients
13
+ */
14
+ export declare class SseHandler {
15
+ private readonly clients;
16
+ /** Get client count */
17
+ get size(): number;
18
+ /** Broadcast event to all matching clients */
19
+ broadcast(event: JobEvent): void;
20
+ /** Create SSE response for a new client */
21
+ createResponse(queueFilter: string | null, corsOrigin: string): Response;
22
+ /** Close all connections */
23
+ closeAll(): void;
24
+ /** Get clients map (for backward compatibility) */
25
+ getClients(): Map<string, SseClient>;
26
+ }
27
+ //# sourceMappingURL=sseHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sseHandler.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/server/sseHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAMzD,0BAA0B;AAC1B,MAAM,WAAW,SAAS;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,+BAA+B,CAAC;IAC5C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IAExD,uBAAuB;IACvB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,8CAA8C;IAC9C,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAoBhC,2CAA2C;IAC3C,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,EAAE,UAAU,EAAE,MAAM,GAAG,QAAQ;IA6BxE,4BAA4B;IAC5B,QAAQ,IAAI,IAAI;IAWhB,mDAAmD;IACnD,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC;CAGrC"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * SSE Handler - Server-Sent Events management
3
+ */
4
+ import { uuid } from '../../shared/hash';
5
+ /** Singleton TextEncoder for SSE messages */
6
+ const textEncoder = new TextEncoder();
7
+ /**
8
+ * SSE Handler - manages Server-Sent Events clients
9
+ */
10
+ export class SseHandler {
11
+ clients = new Map();
12
+ /** Get client count */
13
+ get size() {
14
+ return this.clients.size;
15
+ }
16
+ /** Broadcast event to all matching clients */
17
+ broadcast(event) {
18
+ const message = JSON.stringify(event);
19
+ const sseMessage = `data: ${message}\n\n`;
20
+ const disconnected = [];
21
+ for (const [clientId, client] of this.clients) {
22
+ if (!client.queueFilter || client.queueFilter === event.queue) {
23
+ try {
24
+ client.controller.enqueue(textEncoder.encode(sseMessage));
25
+ }
26
+ catch {
27
+ disconnected.push(clientId);
28
+ }
29
+ }
30
+ }
31
+ for (const clientId of disconnected) {
32
+ this.clients.delete(clientId);
33
+ }
34
+ }
35
+ /** Create SSE response for a new client */
36
+ createResponse(queueFilter, corsOrigin) {
37
+ const clientId = uuid();
38
+ const stream = new ReadableStream({
39
+ start: (controller) => {
40
+ this.clients.set(clientId, {
41
+ id: clientId,
42
+ controller,
43
+ queueFilter,
44
+ });
45
+ controller.enqueue(textEncoder.encode(`data: {"connected":true,"clientId":"${clientId}"}\n\n`));
46
+ },
47
+ cancel: () => {
48
+ this.clients.delete(clientId);
49
+ },
50
+ });
51
+ return new Response(stream, {
52
+ headers: {
53
+ 'Content-Type': 'text/event-stream',
54
+ 'Cache-Control': 'no-cache',
55
+ Connection: 'keep-alive',
56
+ 'Access-Control-Allow-Origin': corsOrigin,
57
+ },
58
+ });
59
+ }
60
+ /** Close all connections */
61
+ closeAll() {
62
+ for (const [, client] of this.clients) {
63
+ try {
64
+ client.controller.close();
65
+ }
66
+ catch {
67
+ // Ignore
68
+ }
69
+ }
70
+ this.clients.clear();
71
+ }
72
+ /** Get clients map (for backward compatibility) */
73
+ getClients() {
74
+ return this.clients;
75
+ }
76
+ }
77
+ //# sourceMappingURL=sseHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sseHandler.js","sourceRoot":"","sources":["../../../src/infrastructure/server/sseHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,6CAA6C;AAC7C,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAStC;;GAEG;AACH,MAAM,OAAO,UAAU;IACJ,OAAO,GAAG,IAAI,GAAG,EAAqB,CAAC;IAExD,uBAAuB;IACvB,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;IAED,8CAA8C;IAC9C,SAAS,CAAC,KAAe;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QACtC,MAAM,UAAU,GAAG,SAAS,OAAO,MAAM,CAAC;QAC1C,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,WAAW,KAAK,KAAK,CAAC,KAAK,EAAE,CAAC;gBAC9D,IAAI,CAAC;oBACH,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,WAAW,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;gBAC5D,CAAC;gBAAC,MAAM,CAAC;oBACP,YAAY,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;QACH,CAAC;QAED,KAAK,MAAM,QAAQ,IAAI,YAAY,EAAE,CAAC;YACpC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAChC,CAAC;IACH,CAAC;IAED,2CAA2C;IAC3C,cAAc,CAAC,WAA0B,EAAE,UAAkB;QAC3D,MAAM,QAAQ,GAAG,IAAI,EAAE,CAAC;QAExB,MAAM,MAAM,GAAG,IAAI,cAAc,CAAC;YAChC,KAAK,EAAE,CAAC,UAAU,EAAE,EAAE;gBACpB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE;oBACzB,EAAE,EAAE,QAAQ;oBACZ,UAAU;oBACV,WAAW;iBACZ,CAAC,CAAC;gBACH,UAAU,CAAC,OAAO,CAChB,WAAW,CAAC,MAAM,CAAC,uCAAuC,QAAQ,QAAQ,CAAC,CAC5E,CAAC;YACJ,CAAC;YACD,MAAM,EAAE,GAAG,EAAE;gBACX,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YAChC,CAAC;SACF,CAAC,CAAC;QAEH,OAAO,IAAI,QAAQ,CAAC,MAAM,EAAE;YAC1B,OAAO,EAAE;gBACP,cAAc,EAAE,mBAAmB;gBACnC,eAAe,EAAE,UAAU;gBAC3B,UAAU,EAAE,YAAY;gBACxB,6BAA6B,EAAE,UAAU;aAC1C;SACF,CAAC,CAAC;IACL,CAAC;IAED,4BAA4B;IAC5B,QAAQ;QACN,KAAK,MAAM,CAAC,EAAE,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACtC,IAAI,CAAC;gBACH,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;YAC5B,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS;YACX,CAAC;QACH,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,mDAAmD;IACnD,UAAU;QACR,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * WebSocket Handler - WebSocket client management
3
+ */
4
+ import type { ServerWebSocket } from 'bun';
5
+ import type { JobEvent } from '../../domain/types/queue';
6
+ import { type HandlerContext } from './handler';
7
+ /** WebSocket client data */
8
+ export interface WsData {
9
+ id: string;
10
+ authenticated: boolean;
11
+ queueFilter: string | null;
12
+ }
13
+ /**
14
+ * WebSocket Handler - manages WebSocket clients and message handling
15
+ */
16
+ export declare class WsHandler {
17
+ private readonly clients;
18
+ /** Get client count */
19
+ get size(): number;
20
+ /** Broadcast event to all matching clients */
21
+ broadcast(event: JobEvent): void;
22
+ /** Handle WebSocket open */
23
+ onOpen(ws: ServerWebSocket<WsData>): void;
24
+ /** Handle WebSocket message */
25
+ onMessage(ws: ServerWebSocket<WsData>, message: string | Buffer, ctx: HandlerContext): Promise<void>;
26
+ /** Handle WebSocket close */
27
+ onClose(ws: ServerWebSocket<WsData>): void;
28
+ /** Get clients map (for backward compatibility) */
29
+ getClients(): Map<string, ServerWebSocket<WsData>>;
30
+ }
31
+ //# sourceMappingURL=wsHandler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsHandler.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/server/wsHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,KAAK,CAAC;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACzD,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAO/D,4BAA4B;AAC5B,MAAM,WAAW,MAAM;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA8C;IAEtE,uBAAuB;IACvB,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,8CAA8C;IAC9C,SAAS,CAAC,KAAK,EAAE,QAAQ,GAAG,IAAI;IAShC,4BAA4B;IAC5B,MAAM,CAAC,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAKzC,+BAA+B;IACzB,SAAS,CACb,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,EAC3B,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC;IAwBhB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,GAAG,IAAI;IAK1C,mDAAmD;IACnD,UAAU,IAAI,GAAG,CAAC,MAAM,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;CAGnD"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * WebSocket Handler - WebSocket client management
3
+ */
4
+ import { handleCommand } from './handler';
5
+ import { parseCommand, serializeResponse, errorResponse } from './protocol';
6
+ import { wsLog } from '../../shared/logger';
7
+ /** Singleton TextDecoder for WebSocket messages */
8
+ const textDecoder = new TextDecoder();
9
+ /**
10
+ * WebSocket Handler - manages WebSocket clients and message handling
11
+ */
12
+ export class WsHandler {
13
+ clients = new Map();
14
+ /** Get client count */
15
+ get size() {
16
+ return this.clients.size;
17
+ }
18
+ /** Broadcast event to all matching clients */
19
+ broadcast(event) {
20
+ const message = JSON.stringify(event);
21
+ for (const [, ws] of this.clients) {
22
+ if (!ws.data.queueFilter || ws.data.queueFilter === event.queue) {
23
+ ws.send(message);
24
+ }
25
+ }
26
+ }
27
+ /** Handle WebSocket open */
28
+ onOpen(ws) {
29
+ this.clients.set(ws.data.id, ws);
30
+ wsLog.info('Client connected', { clientId: ws.data.id });
31
+ }
32
+ /** Handle WebSocket message */
33
+ async onMessage(ws, message, ctx) {
34
+ const text = typeof message === 'string' ? message : textDecoder.decode(message);
35
+ const cmd = parseCommand(text);
36
+ if (!cmd) {
37
+ ws.send(errorResponse('Invalid command'));
38
+ return;
39
+ }
40
+ try {
41
+ const response = await handleCommand(cmd, ctx);
42
+ // Update authentication state
43
+ if (cmd.cmd === 'Auth' && response.ok) {
44
+ ws.data.authenticated = true;
45
+ }
46
+ ws.send(serializeResponse(response));
47
+ }
48
+ catch (err) {
49
+ const msg = err instanceof Error ? err.message : 'Unknown error';
50
+ ws.send(errorResponse(msg, cmd.reqId));
51
+ }
52
+ }
53
+ /** Handle WebSocket close */
54
+ onClose(ws) {
55
+ this.clients.delete(ws.data.id);
56
+ wsLog.info('Client disconnected', { clientId: ws.data.id });
57
+ }
58
+ /** Get clients map (for backward compatibility) */
59
+ getClients() {
60
+ return this.clients;
61
+ }
62
+ }
63
+ //# sourceMappingURL=wsHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wsHandler.js","sourceRoot":"","sources":["../../../src/infrastructure/server/wsHandler.ts"],"names":[],"mappings":"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"}