@pipeline-builder/api-server 3.1.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 (45) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +39 -0
  3. package/lib/api/app-factory.d.ts +71 -0
  4. package/lib/api/app-factory.js +212 -0
  5. package/lib/api/check-quota.d.ts +13 -0
  6. package/lib/api/check-quota.js +67 -0
  7. package/lib/api/context-middleware.d.ts +34 -0
  8. package/lib/api/context-middleware.js +45 -0
  9. package/lib/api/etag-middleware.d.ts +7 -0
  10. package/lib/api/etag-middleware.js +37 -0
  11. package/lib/api/get-context.d.ts +22 -0
  12. package/lib/api/get-context.js +31 -0
  13. package/lib/api/health-checks.d.ts +25 -0
  14. package/lib/api/health-checks.js +36 -0
  15. package/lib/api/idempotency-middleware.d.ts +9 -0
  16. package/lib/api/idempotency-middleware.js +64 -0
  17. package/lib/api/index.d.ts +15 -0
  18. package/lib/api/index.js +43 -0
  19. package/lib/api/metrics.d.ts +12 -0
  20. package/lib/api/metrics.js +83 -0
  21. package/lib/api/middleware-factory.d.ts +47 -0
  22. package/lib/api/middleware-factory.js +66 -0
  23. package/lib/api/middleware.d.ts +1 -0
  24. package/lib/api/middleware.js +14 -0
  25. package/lib/api/quota-helpers.d.ts +23 -0
  26. package/lib/api/quota-helpers.js +25 -0
  27. package/lib/api/request-types.d.ts +55 -0
  28. package/lib/api/request-types.js +62 -0
  29. package/lib/api/require-org-id.d.ts +14 -0
  30. package/lib/api/require-org-id.js +31 -0
  31. package/lib/api/route-wrapper.d.ts +50 -0
  32. package/lib/api/route-wrapper.js +62 -0
  33. package/lib/api/server.d.ts +79 -0
  34. package/lib/api/server.js +144 -0
  35. package/lib/api/tracing.d.ts +15 -0
  36. package/lib/api/tracing.js +53 -0
  37. package/lib/http/index.d.ts +2 -0
  38. package/lib/http/index.js +21 -0
  39. package/lib/http/sse-connection-manager.d.ts +145 -0
  40. package/lib/http/sse-connection-manager.js +329 -0
  41. package/lib/http/ws-manager.d.ts +37 -0
  42. package/lib/http/ws-manager.js +105 -0
  43. package/lib/index.d.ts +30 -0
  44. package/lib/index.js +51 -0
  45. package/package.json +143 -0
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ // Copyright 2026 Pipeline Builder Contributors
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.startServer = startServer;
6
+ exports.runServer = runServer;
7
+ const api_core_1 = require("@pipeline-builder/api-core");
8
+ const pipeline_core_1 = require("@pipeline-builder/pipeline-core");
9
+ const tracing_1 = require("./tracing");
10
+ const logger = (0, api_core_1.createLogger)('Server');
11
+ /**
12
+ * Start an Express server with graceful shutdown handling
13
+ *
14
+ * Features:
15
+ * - Automatic database connection testing
16
+ * - Graceful shutdown on SIGINT/SIGTERM
17
+ * - SSE connection cleanup
18
+ * - Configurable shutdown timeout
19
+ *
20
+ * @param app - Express application
21
+ * @param options - Server options
22
+ * @returns Server instance and control functions
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * const { app, sseManager } = createApp();
27
+ *
28
+ * app.post('/api/resource', requireAuth, handler);
29
+ *
30
+ * await startServer(app, {
31
+ * name: 'My Microservice',
32
+ * sseManager,
33
+ * onStart: (port) => console.log(`Listening on ${port}`),
34
+ * });
35
+ * ```
36
+ */
37
+ async function startServer(app, options = {}) {
38
+ const serverConfig = pipeline_core_1.Config.get('server');
39
+ const { name = 'Microservice', sseManager, shutdownTimeoutMs = 15000, onStart, onShutdown, onBeforeStart, testDatabase, closeDatabase, } = options;
40
+ const port = options.port ?? serverConfig.port;
41
+ // Validate auth configuration at server startup (not during CDK synthesis)
42
+ pipeline_core_1.Config.validateAuth();
43
+ logger.info(`Starting ${name}...`);
44
+ // Pre-start hook (e.g., initialize external connections)
45
+ if (onBeforeStart) {
46
+ await onBeforeStart();
47
+ }
48
+ // Test database connection
49
+ if (testDatabase !== false) {
50
+ const dbHealthy = testDatabase
51
+ ? await testDatabase()
52
+ : await (0, pipeline_core_1.getConnection)().testConnection();
53
+ if (!dbHealthy) {
54
+ throw new Error('Database connection failed');
55
+ }
56
+ logger.info('Database connection established');
57
+ }
58
+ // Start server
59
+ const server = app.listen(port, () => {
60
+ logger.info(`${name} listening on port: ${port}`);
61
+ logger.info(`Platform URL: ${serverConfig.platformUrl}`);
62
+ onStart?.(port);
63
+ });
64
+ // Shutdown handler (guarded against concurrent signals)
65
+ let shuttingDown = false;
66
+ const shutdown = async (signal) => {
67
+ if (shuttingDown)
68
+ return;
69
+ shuttingDown = true;
70
+ if (signal) {
71
+ logger.info(`${signal} received, shutting down gracefully...`);
72
+ }
73
+ // Custom shutdown callback
74
+ if (onShutdown) {
75
+ try {
76
+ await onShutdown();
77
+ }
78
+ catch (error) {
79
+ logger.error('Error in onShutdown callback', { error });
80
+ }
81
+ }
82
+ // Close SSE connections
83
+ if (sseManager) {
84
+ sseManager.shutdown();
85
+ logger.info('SSE connections closed');
86
+ }
87
+ // Close HTTP server
88
+ server.close(async () => {
89
+ logger.info('HTTP server closed');
90
+ // Shutdown OpenTelemetry tracing
91
+ await (0, tracing_1.shutdownTracing)().catch(err => logger.error('Error shutting down tracing', { error: err }));
92
+ // Close database connection
93
+ if (closeDatabase !== false) {
94
+ try {
95
+ if (closeDatabase) {
96
+ await closeDatabase();
97
+ }
98
+ else {
99
+ await (0, pipeline_core_1.closeConnection)();
100
+ }
101
+ logger.info('Database connection closed');
102
+ }
103
+ catch (error) {
104
+ logger.error('Error closing database', { error });
105
+ }
106
+ }
107
+ process.exit(0);
108
+ });
109
+ // Force shutdown after timeout
110
+ setTimeout(() => {
111
+ logger.error('Forced shutdown after timeout');
112
+ process.exit(1);
113
+ }, shutdownTimeoutMs).unref();
114
+ };
115
+ // Register signal handlers
116
+ process.once('SIGINT', () => void shutdown('SIGINT'));
117
+ process.once('SIGTERM', () => void shutdown('SIGTERM'));
118
+ return {
119
+ server,
120
+ port,
121
+ shutdown: () => shutdown(),
122
+ };
123
+ }
124
+ /**
125
+ * Simple server start wrapper with error handling
126
+ *
127
+ * @param app - Express application
128
+ * @param options - Server options
129
+ *
130
+ * @example
131
+ * ```typescript
132
+ * const { app, sseManager } = createApp();
133
+ * app.post('/api/resource', handler);
134
+ *
135
+ * void runServer(app, { name: 'My Service', sseManager });
136
+ * ```
137
+ */
138
+ function runServer(app, options = {}) {
139
+ startServer(app, options).catch((error) => {
140
+ logger.error('Failed to start server', { error });
141
+ process.exit(1);
142
+ });
143
+ }
144
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2FwaS9zZXJ2ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7O0FBeUV0QyxrQ0FnSEM7QUFnQkQsOEJBS0M7QUEzTUQseURBQTBEO0FBQzFELG1FQUF5RjtBQUV6Rix1Q0FBNEM7QUFHNUMsTUFBTSxNQUFNLEdBQUcsSUFBQSx1QkFBWSxFQUFDLFFBQVEsQ0FBQyxDQUFDO0FBc0N0Qzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztHQXlCRztBQUNJLEtBQUssVUFBVSxXQUFXLENBQy9CLEdBQVksRUFDWixVQUE4QixFQUFFO0lBRWhDLE1BQU0sWUFBWSxHQUFHLHNCQUFNLENBQUMsR0FBRyxDQUFDLFFBQVEsQ0FBQyxDQUFDO0lBQzFDLE1BQU0sRUFDSixJQUFJLEdBQUcsY0FBYyxFQUNyQixVQUFVLEVBQ1YsaUJBQWlCLEdBQUcsS0FBSyxFQUN6QixPQUFPLEVBQ1AsVUFBVSxFQUNWLGFBQWEsRUFDYixZQUFZLEVBQ1osYUFBYSxHQUNkLEdBQUcsT0FBTyxDQUFDO0lBQ1osTUFBTSxJQUFJLEdBQUcsT0FBTyxDQUFDLElBQUksSUFBSSxZQUFZLENBQUMsSUFBSSxDQUFDO0lBRS9DLDJFQUEyRTtJQUMzRSxzQkFBTSxDQUFDLFlBQVksRUFBRSxDQUFDO0lBRXRCLE1BQU0sQ0FBQyxJQUFJLENBQUMsWUFBWSxJQUFJLEtBQUssQ0FBQyxDQUFDO0lBRW5DLHlEQUF5RDtJQUN6RCxJQUFJLGFBQWEsRUFBRSxDQUFDO1FBQ2xCLE1BQU0sYUFBYSxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVELDJCQUEyQjtJQUMzQixJQUFJLFlBQVksS0FBSyxLQUFLLEVBQUUsQ0FBQztRQUMzQixNQUFNLFNBQVMsR0FBRyxZQUFZO1lBQzVCLENBQUMsQ0FBQyxNQUFNLFlBQVksRUFBRTtZQUN0QixDQUFDLENBQUMsTUFBTSxJQUFBLDZCQUFhLEdB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Q0FBQztJQUVGLDJCQUEyQjtJQUMzQixPQUFPLENBQUMsSUFBSSxDQUFDLFFBQVEsRUFBRSxHQUFHLEVBQUUsQ0FBQyxLQUFLLFFBQVEsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO0lBQ3RELE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLEdBQUcsRUFBRSxDQUFDLEtBQUssUUFBUSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUM7SUFFeEQsT0FBTztRQUNMLE1BQU07UUFDTixJQUFJO1FBQ0osUUFBUSxFQUFFLEdBQUcsRUFBRSxDQUFDLFFBQVEsRUFBRTtLQUMzQixDQUFDO0FBQ0osQ0FBQztBQUVEOzs7Ozs7Ozs7Ozs7O0dBYUc7QUFDSCxTQUFnQixTQUFTLENBQUMsR0FBWSxFQUFFLFVBQThCLEVBQUU7SUFDdEUsV0FBVyxDQUFDLEdBQUcsRUFBRSxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxLQUFLLEVBQUUsRUFBRTtRQUN4QyxNQUFNLENBQUMsS0FBSyxDQUFDLHdCQUF3QixFQUFFLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztRQUNsRCxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO0lBQ2xCLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBTZXJ2ZXIgfSBmcm9tICdodHRwJztcbmltcG9ydCB7IGNyZWF0ZUxvZ2dlciB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1jb3JlJztcbmltcG9ydCB7IENvbmZpZywgZ2V0Q29ubmVjdGlvbiwgY2xvc2VDb25uZWN0aW9uIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvcGlwZWxpbmUtY29yZSc7XG5pbXBvcnQgeyBFeHByZXNzIH0gZnJvbSAnZXhwcmVzcyc7XG5pbXBvcnQgeyBzaHV0ZG93blRyYWNpbmcgfSBmcm9tICcuL3RyYWNpbmcnO1xuaW1wb3J0IHsgU1NFTWFuYWdlciB9IGZyb20gJy4uL2h0dHAvc3NlLWNvbm5lY3Rpb24tbWFuYWdlcic7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignU2VydmVyJyk7XG5cbi8qKlxuICogT3B0aW9ucyBmb3Igc3RhcnRpbmcgYSBzZXJ2ZXJcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdGFydFNlcnZlck9wdGlvbnMge1xuICAvKiogU2VydmljZSBuYW1lIGZvciBsb2dnaW5nICovXG4gIG5hbWU/OiBzdHJpbmc7XG4gIC8qKiBQb3J0IHRvIGxpc3RlbiBvbiAoZGVmYXVsdDogZnJvbSBjb25maWcpICovXG4gIHBvcnQ/OiBudW1iZXI7XG4gIC8qKiBTU0UgbWFuYWdlciBmb3IgZ3JhY2VmdWwgc2h1dGRvd24gKi9cbiAgc3NlTWFuYWdlcj86IFNTRU1hbmFnZXI7XG4gIC8qKiBTaHV0ZG93biB0aW1lb3V0IGluIG1pbGxpc2Vjb25kcyAoZGVmYXVsdDogMTUwMDApICovXG4gIHNodXRkb3duVGltZW91dE1zPzogbnVtYmVyO1xuICAvKiogQ2FsbGJhY2sgYWZ0ZXIgc2VydmVyIHN0YXJ0cyAqL1xuICBvblN0YXJ0PzogKHBvcnQ6IG51bWJlcikgPT4gdm9pZDtcbiAgLyoqIENhbGxiYWNrIGJlZm9yZSBzaHV0ZG93biAqL1xuICBvblNodXRkb3duPzogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgLyoqIFJ1bnMgYmVmb3JlIGRhdGFiYXNlIGNoZWNrIChlLmcuLCBpbml0aWFsaXplIGV4dGVybmFsIGNvbm5lY3Rpb25zKSAqL1xuICBvbkJlZm9yZVN0YXJ0PzogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbiAgLyoqIEN1c3RvbSBkYXRhYmFzZSBoZWFsdGggY2hlY2ssIG9yIGZhbHNlIHRvIHNraXAuIERlZmF1bHQ6IFBvc3RncmVTUUwgdGVzdENvbm5lY3Rpb24gKi9cbiAgdGVzdERhdGFiYXNlPzogKCgpID0+IFByb21pc2U8Ym9vbGVhbj4pIHwgZmFsc2U7XG4gIC8qKiBDdXN0b20gZGF0YWJhc2UgY2xvc2UsIG9yIGZhbHNlIHRvIHNraXAuIERlZmF1bHQ6IFBvc3RncmVTUUwgY2xvc2VDb25uZWN0aW9uICovXG4gIGNsb3NlRGF0YWJhc2U/OiAoKCkgPT4gUHJvbWlzZTx2b2lkPikgfCBmYWxzZTtcbn1cblxuLyoqXG4gKiBSZXN1bHQgb2Ygc3RhcnRpbmcgYSBzZXJ2ZXJcbiAqL1xuZXhwb3J0IGludGVyZmFjZSBTdGFydFNlcnZlclJlc3VsdCB7XG4gIC8qKiBIVFRQIHNlcnZlciBpbnN0YW5jZSAqL1xuICBzZXJ2ZXI6IFNlcnZlcjtcbiAgLyoqIFBvcnQgdGhlIHNlcnZlciBpcyBsaXN0ZW5pbmcgb24gKi9cbiAgcG9ydDogbnVtYmVyO1xuICAvKiogRnVuY3Rpb24gdG8gbWFudWFsbHkgdHJpZ2dlciBzaHV0ZG93biAqL1xuICBzaHV0ZG93bjogKCkgPT4gUHJvbWlzZTx2b2lkPjtcbn1cblxuLyoqXG4gKiBTdGFydCBhbiBFeHByZXNzIHNlcnZlciB3aXRoIGdyYWNlZnVsIHNodXRkb3duIGhhbmRsaW5nXG4gKlxuICogRmVhdHVyZXM6XG4gKiAtIEF1dG9tYXRpYyBkYXRhYmFzZSBjb25uZWN0aW9uIHRlc3RpbmdcbiAqIC0gR3JhY2VmdWwgc2h1dGRvd24gb24gU0lHSU5UL1NJR1RFUk1cbiAqIC0gU1NFIGNvbm5lY3Rpb24gY2xlYW51cFxuICogLSBDb25maWd1cmFibGUgc2h1dGRvd24gdGltZW91dFxuICpcbiAqIEBwYXJhbSBhcHAgLSBFeHByZXNzIGFwcGxpY2F0aW9uXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFNlcnZlciBvcHRpb25zXG4gKiBAcmV0dXJucyBTZXJ2ZXIgaW5zdGFuY2UgYW5kIGNvbnRyb2wgZnVuY3Rpb25zXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHsgYXBwLCBzc2VNYW5hZ2VyIH0gPSBjcmVhdGVBcHAoKTtcbiAqXG4gKiBhcHAucG9zdCgnL2FwaS9yZXNvdXJjZScsIHJlcXVpcmVBdXRoLCBoYW5kbGVyKTtcbiAqXG4gKiBhd2FpdCBzdGFydFNlcnZlcihhcHAsIHtcbiAqICAgbmFtZTogJ015IE1pY3Jvc2VydmljZScsXG4gKiAgIHNzZU1hbmFnZXIsXG4gKiAgIG9uU3RhcnQ6IChwb3J0KSA9PiBjb25zb2xlLmxvZyhgTGlzdGVuaW5nIG9uICR7cG9ydH1gKSxcbiAqIH0pO1xuICogYGBgXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzdGFydFNlcnZlcihcbiAgYXBwOiBFeHByZXNzLFxuICBvcHRpb25zOiBTdGFydFNlcnZlck9wdGlvbnMgPSB7fSxcbik6IFByb21pc2U8U3RhcnRTZXJ2ZXJSZXN1bHQ+IHtcbiAgY29uc3Qgc2VydmVyQ29uZmlnID0gQ29uZmlnLmdldCgnc2VydmVyJyk7XG4gIGNvbnN0IHtcbiAgICBuYW1lID0gJ01pY3Jvc2VydmljZScsXG4gICAgc3NlTWFuYWdlcixcbiAgICBzaHV0ZG93blRpbWVvdXRNcyA9IDE1MDAwLFxuICAgIG9uU3RhcnQsXG4gICAgb25TaHV0ZG93bixcbiAgICBvbkJlZm9yZVN0YXJ0LFxuICAgIHRlc3REYXRhYmFzZSxcbiAgICBjbG9zZURhdGFiYXNlLFxuICB9ID0gb3B0aW9ucztcbiAgY29uc3QgcG9ydCA9IG9wdGlvbnMucG9ydCA/PyBzZXJ2ZXJDb25maWcucG9ydDtcblxuICAvLyBWYWxpZGF0ZSBhdXRoIGNvbmZpZ3VyYXRpb24gYXQgc2VydmVyIHN0YXJ0dXAgKG5vdCBkdXJpbmcgQ0RLIHN5bnRoZXNpcylcbiAgQ29uZmlnLnZhbGlkYXRlQXV0aCgpO1xuXG4gIGxvZ2dlci5pbmZvKGBTdGFydGluZyAke25hbWV9Li4uYCk7XG5cbiAgLy8gUHJlLXN0YXJ0IGhvb2sgKGUuZy4sIGluaXRpYWxpemUgZXh0ZXJuYWwgY29ubmVjdGlvbnMpXG4gIGlmIChvbkJlZm9yZVN0YXJ0KSB7XG4gICAgYXdhaXQgb25CZWZvcmVTdGFydCgpO1xuICB9XG5cbiAgLy8gVGVzdCBkYXRhYmFzZSBjb25uZWN0aW9uXG4gIGlmICh0ZXN0RGF0YWJhc2UgIT09IGZhbHNlKSB7XG4gICAgY29uc3QgZGJIZWFsdGh5ID0gdGVzdERhdGFiYXNlXG4gICAgICA/IGF3YWl0IHRlc3REYXRhYmFzZSgpXG4gICAgICA6IGF3YWl0IGdldENvbm5lY3Rpb24oKS50ZXN0Q29ubmVjdGlvbigpO1xuXG4gICAgaWYgKCFkYkhlYWx0aHkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRGF0YWJhc2UgY29ubmVjdGlvbiBmYWlsZWQnKTtcbiAgICB9XG5cbiAgICBsb2dnZXIuaW5mbygnRGF0YWJhc2UgY29ubmVjdGlvbiBlc3RhYmxpc2hlZCcpO1xuICB9XG5cbiAgLy8gU3RhcnQgc2VydmVyXG4gIGNvbnN0IHNlcnZlciA9IGFwcC5saXN0ZW4ocG9ydCwgKCkgPT4ge1xuICAgIGxvZ2dlci5pbmZvKGAke25hbWV9IGxpc3RlbmluZyBvbiBwb3J0OiAke3BvcnR9YCk7XG4gICAgbG9nZ2VyLmluZm8oYFBsYXRmb3JtIFVSTDogJHtzZXJ2ZXJDb25maWcucGxhdGZvcm1Vcmx9YCk7XG4gICAgb25TdGFydD8uKHBvcnQpO1xuICB9KTtcblxuICAvLyBTaHV0ZG93biBoYW5kbGVyIChndWFyZGVkIGFnYWluc3QgY29uY3VycmVudCBzaWduYWxzKVxuICBsZXQgc2h1dHRpbmdEb3duID0gZmFsc2U7XG4gIGNvbnN0IHNodXRkb3duID0gYXN5bmMgKHNpZ25hbD86IHN0cmluZykgPT4ge1xuICAgIGlmIChzaHV0dGluZ0Rvd24pIHJldHVybjtcbiAgICBzaHV0dGluZ0Rvd24gPSB0cnVlO1xuXG4gICAgaWYgKHNpZ25hbCkge1xuICAgICAgbG9nZ2VyLmluZm8oYCR7c2lnbmFsfSByZWNlaXZlZCwgc2h1dHRpbmcgZG93biBncmFjZWZ1bGx5Li4uYCk7XG4gICAgfVxuXG4gICAgLy8gQ3VzdG9tIHNodXRkb3duIGNhbGxiYWNrXG4gICAgaWYgKG9uU2h1dGRvd24pIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIGF3YWl0IG9uU2h1dGRvd24oKTtcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIGxvZ2dlci5lcnJvcignRXJyb3IgaW4gb25TaHV0ZG93biBjYWxsYmFjaycsIHsgZXJyb3IgfSk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgLy8gQ2xvc2UgU1NFIGNvbm5lY3Rpb25zXG4gICAgaWYgKHNzZU1hbmFnZXIpIHtcbiAgICAgIHNzZU1hbmFnZXIuc2h1dGRvd24oKTtcbiAgICAgIGxvZ2dlci5pbmZvKCdTU0UgY29ubmVjdGlvbnMgY2xvc2VkJyk7XG4gICAgfVxuXG4gICAgLy8gQ2xvc2UgSFRUUCBzZXJ2ZXJcbiAgICBzZXJ2ZXIuY2xvc2UoYXN5bmMgKCkgPT4ge1xuICAgICAgbG9nZ2VyLmluZm8oJ0hUVFAgc2VydmVyIGNsb3NlZCcpO1xuXG4gICAgICAvLyBTaHV0ZG93biBPcGVuVGVsZW1ldHJ5IHRyYWNpbmdcbiAgICAgIGF3YWl0IHNodXRkb3duVHJhY2luZygpLmNhdGNoKGVyciA9PiBsb2dnZXIuZXJyb3IoJ0Vycm9yIHNodXR0aW5nIGRvd24gdHJhY2luZycsIHsgZXJyb3I6IGVyciB9KSk7XG5cbiAgICAgIC8vIENsb3NlIGRhdGFiYXNlIGNvbm5lY3Rpb25cbiAgICAgIGlmIChjbG9zZURhdGFiYXNlICE9PSBmYWxzZSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGlmIChjbG9zZURhdGFiYXNlKSB7XG4gICAgICAgICAgICBhd2FpdCBjbG9zZURhdGFiYXNlKCk7XG4gICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGF3YWl0IGNsb3NlQ29ubmVjdGlvbigpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBsb2dnZXIuaW5mbygnRGF0YWJhc2UgY29ubmVjdGlvbiBjbG9zZWQnKTtcbiAgICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgICBsb2dnZXIuZXJyb3IoJ0Vycm9yIGNsb3NpbmcgZGF0YWJhc2UnLCB7IGVycm9yIH0pO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHByb2Nlc3MuZXhpdCgwKTtcbiAgICB9KTtcblxuICAgIC8vIEZvcmNlIHNodXRkb3duIGFmdGVyIHRpbWVvdXRcbiAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgIGxvZ2dlci5lcnJvcignRm9yY2VkIHNodXRkb3duIGFmdGVyIHRpbWVvdXQnKTtcbiAgICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgICB9LCBzaHV0ZG93blRpbWVvdXRNcykudW5yZWYoKTtcbiAgfTtcblxuICAvLyBSZWdpc3RlciBzaWduYWwgaGFuZGxlcnNcbiAgcHJvY2Vzcy5vbmNlKCdTSUdJTlQnLCAoKSA9PiB2b2lkIHNodXRkb3duKCdTSUdJTlQnKSk7XG4gIHByb2Nlc3Mub25jZSgnU0lHVEVSTScsICgpID0+IHZvaWQgc2h1dGRvd24oJ1NJR1RFUk0nKSk7XG5cbiAgcmV0dXJuIHtcbiAgICBzZXJ2ZXIsXG4gICAgcG9ydCxcbiAgICBzaHV0ZG93bjogKCkgPT4gc2h1dGRvd24oKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBTaW1wbGUgc2VydmVyIHN0YXJ0IHdyYXBwZXIgd2l0aCBlcnJvciBoYW5kbGluZ1xuICpcbiAqIEBwYXJhbSBhcHAgLSBFeHByZXNzIGFwcGxpY2F0aW9uXG4gKiBAcGFyYW0gb3B0aW9ucyAtIFNlcnZlciBvcHRpb25zXG4gKlxuICogQGV4YW1wbGVcbiAqIGBgYHR5cGVzY3JpcHRcbiAqIGNvbnN0IHsgYXBwLCBzc2VNYW5hZ2VyIH0gPSBjcmVhdGVBcHAoKTtcbiAqIGFwcC5wb3N0KCcvYXBpL3Jlc291cmNlJywgaGFuZGxlcik7XG4gKlxuICogdm9pZCBydW5TZXJ2ZXIoYXBwLCB7IG5hbWU6ICdNeSBTZXJ2aWNlJywgc3NlTWFuYWdlciB9KTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gcnVuU2VydmVyKGFwcDogRXhwcmVzcywgb3B0aW9uczogU3RhcnRTZXJ2ZXJPcHRpb25zID0ge30pOiB2b2lkIHtcbiAgc3RhcnRTZXJ2ZXIoYXBwLCBvcHRpb25zKS5jYXRjaCgoZXJyb3IpID0+IHtcbiAgICBsb2dnZXIuZXJyb3IoJ0ZhaWxlZCB0byBzdGFydCBzZXJ2ZXInLCB7IGVycm9yIH0pO1xuICAgIHByb2Nlc3MuZXhpdCgxKTtcbiAgfSk7XG59XG4iXX0=
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Initialize OpenTelemetry tracing with OTLP HTTP exporter.
3
+ * Configured via `Config.get('observability').tracing`.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * import { initTracing } from '@pipeline-builder/api-server';
8
+ * initTracing('pipeline-service');
9
+ * ```
10
+ */
11
+ export declare function initTracing(serviceName: string): void;
12
+ /**
13
+ * Shutdown the OpenTelemetry SDK gracefully.
14
+ */
15
+ export declare function shutdownTracing(): Promise<void>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ // Copyright 2026 Pipeline Builder Contributors
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.initTracing = initTracing;
6
+ exports.shutdownTracing = shutdownTracing;
7
+ const exporter_trace_otlp_http_1 = require("@opentelemetry/exporter-trace-otlp-http");
8
+ const resources_1 = require("@opentelemetry/resources");
9
+ const sdk_node_1 = require("@opentelemetry/sdk-node");
10
+ const api_core_1 = require("@pipeline-builder/api-core");
11
+ const pipeline_core_1 = require("@pipeline-builder/pipeline-core");
12
+ const logger = (0, api_core_1.createLogger)('Tracing');
13
+ let sdk = null;
14
+ /**
15
+ * Initialize OpenTelemetry tracing with OTLP HTTP exporter.
16
+ * Configured via `Config.get('observability').tracing`.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { initTracing } from '@pipeline-builder/api-server';
21
+ * initTracing('pipeline-service');
22
+ * ```
23
+ */
24
+ function initTracing(serviceName) {
25
+ const { tracing } = pipeline_core_1.Config.getAny('observability');
26
+ if (!tracing.enabled) {
27
+ logger.debug('OpenTelemetry tracing disabled (set OTEL_TRACING_ENABLED=true to enable)');
28
+ return;
29
+ }
30
+ if (sdk) {
31
+ logger.debug('OpenTelemetry already initialized');
32
+ return;
33
+ }
34
+ const exporter = new exporter_trace_otlp_http_1.OTLPTraceExporter({
35
+ url: tracing.endpoint,
36
+ });
37
+ sdk = new sdk_node_1.NodeSDK({
38
+ resource: (0, resources_1.resourceFromAttributes)({ 'service.name': serviceName }),
39
+ traceExporter: exporter,
40
+ });
41
+ sdk.start();
42
+ logger.info(`OpenTelemetry tracing initialized for ${serviceName}`);
43
+ }
44
+ /**
45
+ * Shutdown the OpenTelemetry SDK gracefully.
46
+ */
47
+ async function shutdownTracing() {
48
+ if (sdk) {
49
+ await sdk.shutdown();
50
+ sdk = null;
51
+ }
52
+ }
53
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhY2luZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hcGkvdHJhY2luZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0NBQStDO0FBQy9DLHNDQUFzQzs7QUFzQnRDLGtDQXdCQztBQUtELDBDQUtDO0FBdERELHNGQUE0RTtBQUM1RSx3REFBa0U7QUFDbEUsc0RBQWtEO0FBQ2xELHlEQUEwRDtBQUMxRCxtRUFBeUQ7QUFFekQsTUFBTSxNQUFNLEdBQUcsSUFBQSx1QkFBWSxFQUFDLFNBQVMsQ0FBQyxDQUFDO0FBRXZDLElBQUksR0FBRyxHQUFtQixJQUFJLENBQUM7QUFFL0I7Ozs7Ozs7OztHQVNHO0FBQ0gsU0FBZ0IsV0FBVyxDQUFDLFdBQW1CO0lBQzdDLE1BQU0sRUFBRSxPQUFPLEVBQUUsR0FBRyxzQkFBTSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQXdELENBQUM7SUFFMUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxPQUFPLEVBQUUsQ0FBQztRQUNyQixNQUFNLENBQUMsS0FBSyxDQUFDLDBFQUEwRSxDQUFDLENBQUM7UUFDekYsT0FBTztJQUNULENBQUM7SUFFRCxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ1IsTUFBTSxDQUFDLEtBQUssQ0FBQyxtQ0FBbUMsQ0FBQyxDQUFDO1FBQ2xELE9BQU87SUFDVCxDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsSUFBSSw0Q0FBaUIsQ0FBQztRQUNyQyxHQUFHLEVBQUUsT0FBTyxDQUFDLFFBQVE7S0FDdEIsQ0FBQyxDQUFDO0lBRUgsR0FBRyxHQUFHLElBQUksa0JBQU8sQ0FBQztRQUNoQixRQUFRLEVBQUUsSUFBQSxrQ0FBc0IsRUFBQyxFQUFFLGNBQWMsRUFBRSxXQUFXLEVBQUUsQ0FBQztRQUNqRSxhQUFhLEVBQUUsUUFBUTtLQUN4QixDQUFDLENBQUM7SUFFSCxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7SUFDWixNQUFNLENBQUMsSUFBSSxDQUFDLHlDQUF5QyxXQUFXLEVBQUUsQ0FBQyxDQUFDO0FBQ3RFLENBQUM7QUFFRDs7R0FFRztBQUNJLEtBQUssVUFBVSxlQUFlO0lBQ25DLElBQUksR0FBRyxFQUFFLENBQUM7UUFDUixNQUFNLEdBQUcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUNyQixHQUFHLEdBQUcsSUFBSSxDQUFDO0lBQ2IsQ0FBQztBQUNILENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHsgT1RMUFRyYWNlRXhwb3J0ZXIgfSBmcm9tICdAb3BlbnRlbGVtZXRyeS9leHBvcnRlci10cmFjZS1vdGxwLWh0dHAnO1xuaW1wb3J0IHsgcmVzb3VyY2VGcm9tQXR0cmlidXRlcyB9IGZyb20gJ0BvcGVudGVsZW1ldHJ5L3Jlc291cmNlcyc7XG5pbXBvcnQgeyBOb2RlU0RLIH0gZnJvbSAnQG9wZW50ZWxlbWV0cnkvc2RrLW5vZGUnO1xuaW1wb3J0IHsgY3JlYXRlTG9nZ2VyIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvYXBpLWNvcmUnO1xuaW1wb3J0IHsgQ29uZmlnIH0gZnJvbSAnQHBpcGVsaW5lLWJ1aWxkZXIvcGlwZWxpbmUtY29yZSc7XG5cbmNvbnN0IGxvZ2dlciA9IGNyZWF0ZUxvZ2dlcignVHJhY2luZycpO1xuXG5sZXQgc2RrOiBOb2RlU0RLIHwgbnVsbCA9IG51bGw7XG5cbi8qKlxuICogSW5pdGlhbGl6ZSBPcGVuVGVsZW1ldHJ5IHRyYWNpbmcgd2l0aCBPVExQIEhUVFAgZXhwb3J0ZXIuXG4gKiBDb25maWd1cmVkIHZpYSBgQ29uZmlnLmdldCgnb2JzZXJ2YWJpbGl0eScpLnRyYWNpbmdgLlxuICpcbiAqIEBleGFtcGxlXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBpbml0VHJhY2luZyB9IGZyb20gJ0BwaXBlbGluZS1idWlsZGVyL2FwaS1zZXJ2ZXInO1xuICogaW5pdFRyYWNpbmcoJ3BpcGVsaW5lLXNlcnZpY2UnKTtcbiAqIGBgYFxuICovXG5leHBvcnQgZnVuY3Rpb24gaW5pdFRyYWNpbmcoc2VydmljZU5hbWU6IHN0cmluZyk6IHZvaWQge1xuICBjb25zdCB7IHRyYWNpbmcgfSA9IENvbmZpZy5nZXRBbnkoJ29ic2VydmFiaWxpdHknKSBhcyB7IHRyYWNpbmc6IHsgZW5hYmxlZDogYm9vbGVhbjsgZW5kcG9pbnQ6IHN0cmluZyB9IH07XG5cbiAgaWYgKCF0cmFjaW5nLmVuYWJsZWQpIHtcbiAgICBsb2dnZXIuZGVidWcoJ09wZW5UZWxlbWV0cnkgdHJhY2luZyBkaXNhYmxlZCAoc2V0IE9URUxfVFJBQ0lOR19FTkFCTEVEPXRydWUgdG8gZW5hYmxlKScpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGlmIChzZGspIHtcbiAgICBsb2dnZXIuZGVidWcoJ09wZW5UZWxlbWV0cnkgYWxyZWFkeSBpbml0aWFsaXplZCcpO1xuICAgIHJldHVybjtcbiAgfVxuXG4gIGNvbnN0IGV4cG9ydGVyID0gbmV3IE9UTFBUcmFjZUV4cG9ydGVyKHtcbiAgICB1cmw6IHRyYWNpbmcuZW5kcG9pbnQsXG4gIH0pO1xuXG4gIHNkayA9IG5ldyBOb2RlU0RLKHtcbiAgICByZXNvdXJjZTogcmVzb3VyY2VGcm9tQXR0cmlidXRlcyh7ICdzZXJ2aWNlLm5hbWUnOiBzZXJ2aWNlTmFtZSB9KSxcbiAgICB0cmFjZUV4cG9ydGVyOiBleHBvcnRlcixcbiAgfSk7XG5cbiAgc2RrLnN0YXJ0KCk7XG4gIGxvZ2dlci5pbmZvKGBPcGVuVGVsZW1ldHJ5IHRyYWNpbmcgaW5pdGlhbGl6ZWQgZm9yICR7c2VydmljZU5hbWV9YCk7XG59XG5cbi8qKlxuICogU2h1dGRvd24gdGhlIE9wZW5UZWxlbWV0cnkgU0RLIGdyYWNlZnVsbHkuXG4gKi9cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBzaHV0ZG93blRyYWNpbmcoKTogUHJvbWlzZTx2b2lkPiB7XG4gIGlmIChzZGspIHtcbiAgICBhd2FpdCBzZGsuc2h1dGRvd24oKTtcbiAgICBzZGsgPSBudWxsO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,2 @@
1
+ export * from './sse-connection-manager';
2
+ export * from './ws-manager';
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ // Copyright 2026 Pipeline Builder Contributors
3
+ // SPDX-License-Identifier: Apache-2.0
4
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
5
+ if (k2 === undefined) k2 = k;
6
+ var desc = Object.getOwnPropertyDescriptor(m, k);
7
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
8
+ desc = { enumerable: true, get: function() { return m[k]; } };
9
+ }
10
+ Object.defineProperty(o, k2, desc);
11
+ }) : (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ o[k2] = m[k];
14
+ }));
15
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
17
+ };
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ __exportStar(require("./sse-connection-manager"), exports);
20
+ __exportStar(require("./ws-manager"), exports);
21
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaHR0cC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsK0NBQStDO0FBQy9DLHNDQUFzQzs7Ozs7Ozs7Ozs7Ozs7OztBQUV0QywyREFBeUM7QUFDekMsK0NBQTZCIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQ29weXJpZ2h0IDIwMjYgUGlwZWxpbmUgQnVpbGRlciBDb250cmlidXRvcnNcbi8vIFNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBcGFjaGUtMi4wXG5cbmV4cG9ydCAqIGZyb20gJy4vc3NlLWNvbm5lY3Rpb24tbWFuYWdlcic7XG5leHBvcnQgKiBmcm9tICcuL3dzLW1hbmFnZXInO1xuIl19
@@ -0,0 +1,145 @@
1
+ import { Response } from 'express';
2
+ /**
3
+ * Event types for SSE logging
4
+ */
5
+ export type SSEEventType = 'INFO' | 'WARN' | 'ERROR' | 'COMPLETED' | 'ROLLBACK' | 'MESSAGE';
6
+ /**
7
+ * SSE payload structure
8
+ */
9
+ export interface SSEPayload {
10
+ ts: string;
11
+ type: SSEEventType;
12
+ message: string;
13
+ data?: unknown;
14
+ }
15
+ /**
16
+ * SSE client with connection tracking
17
+ */
18
+ export interface SSEClient {
19
+ id: string;
20
+ res: Response;
21
+ connectedAt: number;
22
+ timeout: NodeJS.Timeout;
23
+ /** Number of consecutive backpressure events (write returned false). */
24
+ backpressureCount: number;
25
+ }
26
+ /**
27
+ * SSE Manager configuration options
28
+ */
29
+ export interface SSEManagerOptions {
30
+ /** Maximum clients allowed per request ID (default: 10) */
31
+ maxClientsPerRequest?: number;
32
+ /** Client timeout in milliseconds (default: 30 minutes) */
33
+ clientTimeoutMs?: number;
34
+ /** Interval for cleanup checks in milliseconds (default: 5 minutes) */
35
+ cleanupIntervalMs?: number;
36
+ }
37
+ /**
38
+ * SSE Manager statistics
39
+ */
40
+ export interface SSEManagerStats {
41
+ totalRequests: number;
42
+ totalClients: number;
43
+ oldestConnectionMs: number | null;
44
+ }
45
+ /**
46
+ * SSE helper class with memory leak protection
47
+ *
48
+ * Features:
49
+ * - Client limits per request ID
50
+ * - Automatic timeout for idle connections
51
+ * - Periodic cleanup of stale connections
52
+ * - Connection statistics
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const sseManager = new SSEManager({ maxClientsPerRequest: 5 });
57
+ * app.get('/logs/:requestId', sseManager.middleware());
58
+ *
59
+ * // Send events
60
+ * sseManager.send('request-123', 'INFO', 'Processing...');
61
+ * ```
62
+ */
63
+ export declare class SSEManager {
64
+ private clients;
65
+ private readonly maxClientsPerRequest;
66
+ private readonly clientTimeoutMs;
67
+ private cleanupInterval;
68
+ constructor(options?: SSEManagerOptions);
69
+ /**
70
+ * Adds a client to the SSE manager
71
+ *
72
+ * @param requestId - Unique request ID
73
+ * @param res - Express Response object
74
+ * @returns true if client was added, false if rejected (limit reached)
75
+ */
76
+ addClient(requestId: string, res: Response): boolean;
77
+ /**
78
+ * Removes a client from the manager
79
+ */
80
+ private removeClient;
81
+ /**
82
+ * Sends a message to all SSE clients for a requestId
83
+ *
84
+ * @param requestId - Request ID
85
+ * @param type - Event type
86
+ * @param message - Message string
87
+ * @param data - Optional additional data
88
+ * @returns Number of clients the message was sent to
89
+ */
90
+ send(requestId: string, type: SSEEventType, message: string, data?: unknown): number;
91
+ /**
92
+ * Broadcast a message to all connected clients across all requests
93
+ *
94
+ * @param type - Event type
95
+ * @param message - Message string
96
+ * @param data - Optional additional data
97
+ * @returns Total number of clients the message was sent to
98
+ */
99
+ broadcast(type: SSEEventType, message: string, data?: unknown): number;
100
+ /**
101
+ * Close all clients for a specific request
102
+ *
103
+ * @param requestId - Request ID to close
104
+ * @param finalMessage - Optional final message to send before closing
105
+ */
106
+ closeRequest(requestId: string, finalMessage?: string): void;
107
+ /**
108
+ * Get statistics about current connections
109
+ */
110
+ getStats(): SSEManagerStats;
111
+ /**
112
+ * Check if a request has any connected clients
113
+ */
114
+ hasClients(requestId: string): boolean;
115
+ /**
116
+ * Get the number of clients for a specific request
117
+ */
118
+ getClientCount(requestId: string): number;
119
+ /**
120
+ * Middleware to initialize SSE connection
121
+ *
122
+ * @example
123
+ * ```typescript
124
+ * app.get('/logs/:requestId', sseManager.middleware());
125
+ * ```
126
+ */
127
+ middleware(): (req: {
128
+ params: {
129
+ requestId: string;
130
+ };
131
+ }, res: Response) => void;
132
+ /**
133
+ * Start periodic cleanup of stale connections
134
+ */
135
+ private startCleanupInterval;
136
+ /**
137
+ * Clean up stale connections using single-pass partition.
138
+ * O(R × C) instead of O(R × C²), and avoids mutation-during-iteration.
139
+ */
140
+ private cleanup;
141
+ /**
142
+ * Shutdown the SSE manager and close all connections
143
+ */
144
+ shutdown(): void;
145
+ }