dspx 0.1.1-alpha.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.
- package/.github/workflows/ci.yml +185 -0
- package/.vscode/c_cpp_properties.json +17 -0
- package/.vscode/settings.json +68 -0
- package/.vscode/tasks.json +28 -0
- package/DISCLAIMER.md +32 -0
- package/LICENSE +21 -0
- package/README.md +1803 -0
- package/ROADMAP.md +192 -0
- package/TECHNICAL_DEBT.md +165 -0
- package/binding.gyp +65 -0
- package/docs/ADVANCED_LOGGER_FEATURES.md +598 -0
- package/docs/AUTHENTICATION_SECURITY.md +396 -0
- package/docs/BACKEND_IMPROVEMENTS.md +399 -0
- package/docs/CHEBYSHEV_BIQUAD_EQ_IMPLEMENTATION.md +405 -0
- package/docs/FFT_IMPLEMENTATION.md +490 -0
- package/docs/FFT_IMPROVEMENTS_SUMMARY.md +387 -0
- package/docs/FFT_USER_GUIDE.md +494 -0
- package/docs/FILTERS_IMPLEMENTATION.md +260 -0
- package/docs/FILTER_API_GUIDE.md +418 -0
- package/docs/FIR_SIMD_OPTIMIZATION.md +175 -0
- package/docs/LOGGER_API_REFERENCE.md +350 -0
- package/docs/NOTCH_FILTER_QUICK_REF.md +121 -0
- package/docs/PHASE2_TESTS_AND_NOTCH_FILTER.md +341 -0
- package/docs/PHASES_5_7_SUMMARY.md +403 -0
- package/docs/PIPELINE_FILTER_INTEGRATION.md +446 -0
- package/docs/SIMD_OPTIMIZATIONS.md +211 -0
- package/docs/TEST_MIGRATION_SUMMARY.md +173 -0
- package/docs/TIMESERIES_IMPLEMENTATION_SUMMARY.md +322 -0
- package/docs/TIMESERIES_QUICK_REF.md +85 -0
- package/docs/advanced.md +559 -0
- package/docs/time-series-guide.md +617 -0
- package/docs/time-series-migration.md +376 -0
- package/jest.config.js +37 -0
- package/package.json +42 -0
- package/prebuilds/linux-x64/dsp-ts-redis.node +0 -0
- package/prebuilds/win32-x64/dsp-ts-redis.node +0 -0
- package/scripts/test.js +24 -0
- package/src/build/dsp-ts-redis.node +0 -0
- package/src/native/DspPipeline.cc +675 -0
- package/src/native/DspPipeline.h +44 -0
- package/src/native/FftBindings.cc +817 -0
- package/src/native/FilterBindings.cc +1001 -0
- package/src/native/IDspStage.h +53 -0
- package/src/native/adapters/InterpolatorStage.h +201 -0
- package/src/native/adapters/MeanAbsoluteValueStage.h +289 -0
- package/src/native/adapters/MovingAverageStage.h +306 -0
- package/src/native/adapters/RectifyStage.h +88 -0
- package/src/native/adapters/ResamplerStage.h +238 -0
- package/src/native/adapters/RmsStage.h +299 -0
- package/src/native/adapters/SscStage.h +121 -0
- package/src/native/adapters/VarianceStage.h +307 -0
- package/src/native/adapters/WampStage.h +114 -0
- package/src/native/adapters/WaveformLengthStage.h +115 -0
- package/src/native/adapters/ZScoreNormalizeStage.h +326 -0
- package/src/native/core/FftEngine.cc +441 -0
- package/src/native/core/FftEngine.h +224 -0
- package/src/native/core/FirFilter.cc +324 -0
- package/src/native/core/FirFilter.h +149 -0
- package/src/native/core/IirFilter.cc +576 -0
- package/src/native/core/IirFilter.h +210 -0
- package/src/native/core/MovingAbsoluteValueFilter.cc +17 -0
- package/src/native/core/MovingAbsoluteValueFilter.h +135 -0
- package/src/native/core/MovingAverageFilter.cc +18 -0
- package/src/native/core/MovingAverageFilter.h +135 -0
- package/src/native/core/MovingFftFilter.cc +291 -0
- package/src/native/core/MovingFftFilter.h +203 -0
- package/src/native/core/MovingVarianceFilter.cc +194 -0
- package/src/native/core/MovingVarianceFilter.h +114 -0
- package/src/native/core/MovingZScoreFilter.cc +215 -0
- package/src/native/core/MovingZScoreFilter.h +113 -0
- package/src/native/core/Policies.h +352 -0
- package/src/native/core/RmsFilter.cc +18 -0
- package/src/native/core/RmsFilter.h +131 -0
- package/src/native/core/SscFilter.cc +16 -0
- package/src/native/core/SscFilter.h +137 -0
- package/src/native/core/WampFilter.cc +16 -0
- package/src/native/core/WampFilter.h +101 -0
- package/src/native/core/WaveformLengthFilter.cc +17 -0
- package/src/native/core/WaveformLengthFilter.h +98 -0
- package/src/native/utils/CircularBufferArray.cc +336 -0
- package/src/native/utils/CircularBufferArray.h +62 -0
- package/src/native/utils/CircularBufferVector.cc +145 -0
- package/src/native/utils/CircularBufferVector.h +45 -0
- package/src/native/utils/NapiUtils.cc +53 -0
- package/src/native/utils/NapiUtils.h +21 -0
- package/src/native/utils/SimdOps.h +870 -0
- package/src/native/utils/SlidingWindowFilter.cc +239 -0
- package/src/native/utils/SlidingWindowFilter.h +159 -0
- package/src/native/utils/TimeSeriesBuffer.cc +205 -0
- package/src/native/utils/TimeSeriesBuffer.h +140 -0
- package/src/ts/CircularLogBuffer.ts +87 -0
- package/src/ts/DriftDetector.ts +331 -0
- package/src/ts/TopicRouter.ts +428 -0
- package/src/ts/__tests__/AdvancedDsp.test.ts +585 -0
- package/src/ts/__tests__/AuthAndEdgeCases.test.ts +241 -0
- package/src/ts/__tests__/Chaining.test.ts +387 -0
- package/src/ts/__tests__/ChebyshevBiquad.test.ts +229 -0
- package/src/ts/__tests__/CircularLogBuffer.test.ts +158 -0
- package/src/ts/__tests__/DriftDetector.test.ts +389 -0
- package/src/ts/__tests__/Fft.test.ts +484 -0
- package/src/ts/__tests__/ListState.test.ts +153 -0
- package/src/ts/__tests__/Logger.test.ts +208 -0
- package/src/ts/__tests__/LoggerAdvanced.test.ts +319 -0
- package/src/ts/__tests__/LoggerMinor.test.ts +247 -0
- package/src/ts/__tests__/MeanAbsoluteValue.test.ts +398 -0
- package/src/ts/__tests__/MovingAverage.test.ts +322 -0
- package/src/ts/__tests__/RMS.test.ts +315 -0
- package/src/ts/__tests__/Rectify.test.ts +272 -0
- package/src/ts/__tests__/Redis.test.ts +456 -0
- package/src/ts/__tests__/SlopeSignChange.test.ts +166 -0
- package/src/ts/__tests__/Tap.test.ts +164 -0
- package/src/ts/__tests__/TimeBasedExpiration.test.ts +124 -0
- package/src/ts/__tests__/TimeBasedRmsAndMav.test.ts +231 -0
- package/src/ts/__tests__/TimeBasedVarianceAndZScore.test.ts +284 -0
- package/src/ts/__tests__/TimeSeries.test.ts +254 -0
- package/src/ts/__tests__/TopicRouter.test.ts +332 -0
- package/src/ts/__tests__/TopicRouterAdvanced.test.ts +483 -0
- package/src/ts/__tests__/TopicRouterPriority.test.ts +487 -0
- package/src/ts/__tests__/Variance.test.ts +509 -0
- package/src/ts/__tests__/WaveformLength.test.ts +147 -0
- package/src/ts/__tests__/WillisonAmplitude.test.ts +197 -0
- package/src/ts/__tests__/ZScoreNormalize.test.ts +459 -0
- package/src/ts/advanced-dsp.ts +566 -0
- package/src/ts/backends.ts +1137 -0
- package/src/ts/bindings.ts +1225 -0
- package/src/ts/easter-egg.ts +42 -0
- package/src/ts/examples/MeanAbsoluteValue/test-state.ts +99 -0
- package/src/ts/examples/MeanAbsoluteValue/test-streaming.ts +269 -0
- package/src/ts/examples/MovingAverage/test-state.ts +85 -0
- package/src/ts/examples/MovingAverage/test-streaming.ts +188 -0
- package/src/ts/examples/RMS/test-state.ts +97 -0
- package/src/ts/examples/RMS/test-streaming.ts +253 -0
- package/src/ts/examples/Rectify/test-state.ts +107 -0
- package/src/ts/examples/Rectify/test-streaming.ts +242 -0
- package/src/ts/examples/Variance/test-state.ts +195 -0
- package/src/ts/examples/Variance/test-streaming.ts +260 -0
- package/src/ts/examples/ZScoreNormalize/test-state.ts +277 -0
- package/src/ts/examples/ZScoreNormalize/test-streaming.ts +306 -0
- package/src/ts/examples/advanced-dsp-examples.ts +397 -0
- package/src/ts/examples/callbacks/advanced-router-features.ts +326 -0
- package/src/ts/examples/callbacks/benchmark-circular-buffer.ts +109 -0
- package/src/ts/examples/callbacks/monitoring-example.ts +265 -0
- package/src/ts/examples/callbacks/pipeline-callbacks-example.ts +137 -0
- package/src/ts/examples/callbacks/pooled-callbacks-example.ts +274 -0
- package/src/ts/examples/callbacks/priority-routing-example.ts +277 -0
- package/src/ts/examples/callbacks/production-topic-router.ts +214 -0
- package/src/ts/examples/callbacks/topic-based-logging.ts +161 -0
- package/src/ts/examples/chaining/test-chaining-redis.ts +113 -0
- package/src/ts/examples/chaining/test-chaining.ts +52 -0
- package/src/ts/examples/emg-features-example.ts +284 -0
- package/src/ts/examples/fft-example.ts +309 -0
- package/src/ts/examples/fft-examples.ts +349 -0
- package/src/ts/examples/filter-examples.ts +320 -0
- package/src/ts/examples/list-state-example.ts +131 -0
- package/src/ts/examples/logger-example.ts +91 -0
- package/src/ts/examples/notch-filter-examples.ts +243 -0
- package/src/ts/examples/phase5/drift-detection-example.ts +290 -0
- package/src/ts/examples/phase6-7/production-observability.ts +476 -0
- package/src/ts/examples/phase6-7/redis-timeseries-integration.ts +446 -0
- package/src/ts/examples/redis/redis-example.ts +202 -0
- package/src/ts/examples/redis-example.ts +202 -0
- package/src/ts/examples/simd-benchmark.ts +126 -0
- package/src/ts/examples/tap-debugging.ts +230 -0
- package/src/ts/examples/timeseries/comparison-example.ts +290 -0
- package/src/ts/examples/timeseries/iot-sensor-example.ts +143 -0
- package/src/ts/examples/timeseries/redis-streaming-example.ts +233 -0
- package/src/ts/examples/waveform-length-example.ts +139 -0
- package/src/ts/fft.ts +722 -0
- package/src/ts/filters.ts +1078 -0
- package/src/ts/index.ts +120 -0
- package/src/ts/types.ts +589 -0
- package/tsconfig.json +15 -0
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topic-based log router for fan-out to multiple observability backends
|
|
3
|
+
*
|
|
4
|
+
* Implements production-grade routing pattern used by:
|
|
5
|
+
* - Grafana Loki
|
|
6
|
+
* - OpenTelemetry Collector
|
|
7
|
+
* - FluentBit
|
|
8
|
+
* - Vector.dev
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { LogEntry, LogPriority } from "./types.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Route handler function for processing log entries
|
|
15
|
+
*/
|
|
16
|
+
export type RouteHandler = (log: LogEntry) => Promise<void> | void;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Custom pattern matcher function
|
|
20
|
+
*/
|
|
21
|
+
export type PatternMatcher = (topic: string) => boolean;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Route configuration options
|
|
25
|
+
*/
|
|
26
|
+
export interface RouteOptions {
|
|
27
|
+
/** Maximum number of concurrent executions for this route (default: unlimited) */
|
|
28
|
+
concurrency?: number;
|
|
29
|
+
/** Enable latency tracking for this route (default: false) */
|
|
30
|
+
trackMetrics?: boolean;
|
|
31
|
+
/** Minimum priority level for logs to be routed (1-10, default: 1 = all logs) */
|
|
32
|
+
minPriority?: LogPriority;
|
|
33
|
+
/** Maximum priority level for logs to be routed (1-10, default: 10 = all logs) */
|
|
34
|
+
maxPriority?: LogPriority;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Route metrics for observability
|
|
39
|
+
*/
|
|
40
|
+
export interface RouteMetrics {
|
|
41
|
+
name: string;
|
|
42
|
+
pattern: string;
|
|
43
|
+
executionCount: number;
|
|
44
|
+
totalDuration: number;
|
|
45
|
+
averageDuration: number;
|
|
46
|
+
minDuration: number;
|
|
47
|
+
maxDuration: number;
|
|
48
|
+
errorCount: number;
|
|
49
|
+
lastExecuted?: number;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Route definition with pattern matching
|
|
54
|
+
*/
|
|
55
|
+
export interface Route {
|
|
56
|
+
pattern: RegExp;
|
|
57
|
+
handler: RouteHandler;
|
|
58
|
+
name?: string;
|
|
59
|
+
options: RouteOptions;
|
|
60
|
+
metrics: {
|
|
61
|
+
count: number;
|
|
62
|
+
totalDuration: number;
|
|
63
|
+
minDuration: number;
|
|
64
|
+
maxDuration: number;
|
|
65
|
+
errorCount: number;
|
|
66
|
+
lastExecuted?: number;
|
|
67
|
+
};
|
|
68
|
+
semaphore?: {
|
|
69
|
+
running: number;
|
|
70
|
+
queue: Array<() => void>;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* TopicRouter - Fan-out logs to multiple backends based on topic patterns
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* const router = new TopicRouter();
|
|
80
|
+
*
|
|
81
|
+
* // Add routes
|
|
82
|
+
* router.addRoute(/^pipeline\.error/, async (log) => {
|
|
83
|
+
* await pagerDuty.alert(log);
|
|
84
|
+
* });
|
|
85
|
+
*
|
|
86
|
+
* router.addRoute(/^pipeline\.performance/, async (log) => {
|
|
87
|
+
* await prometheus.record(log.topic, log.context);
|
|
88
|
+
* });
|
|
89
|
+
*
|
|
90
|
+
* router.addRoute(/./, async (log) => {
|
|
91
|
+
* await loki.send(log);
|
|
92
|
+
* });
|
|
93
|
+
*
|
|
94
|
+
* // Use with pipeline
|
|
95
|
+
* pipeline.pipeline({
|
|
96
|
+
* onLogBatch: (logs) => router.routeBatch(logs)
|
|
97
|
+
* });
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
export class TopicRouter {
|
|
101
|
+
private routes: Route[] = [];
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Add a route with pattern matching
|
|
105
|
+
* @param pattern - Regex or string pattern to match against log.topic
|
|
106
|
+
* @param handler - Handler function to process matching logs
|
|
107
|
+
* @param nameOrOptions - Optional name or route options
|
|
108
|
+
*/
|
|
109
|
+
addRoute(
|
|
110
|
+
pattern: RegExp | string,
|
|
111
|
+
handler: RouteHandler,
|
|
112
|
+
nameOrOptions?: string | RouteOptions
|
|
113
|
+
): this {
|
|
114
|
+
const name = typeof nameOrOptions === "string" ? nameOrOptions : undefined;
|
|
115
|
+
const options: RouteOptions =
|
|
116
|
+
typeof nameOrOptions === "object" ? nameOrOptions : {};
|
|
117
|
+
|
|
118
|
+
this.routes.push({
|
|
119
|
+
pattern: typeof pattern === "string" ? new RegExp(pattern) : pattern,
|
|
120
|
+
handler,
|
|
121
|
+
name,
|
|
122
|
+
options,
|
|
123
|
+
metrics: {
|
|
124
|
+
count: 0,
|
|
125
|
+
totalDuration: 0,
|
|
126
|
+
minDuration: Infinity,
|
|
127
|
+
maxDuration: 0,
|
|
128
|
+
errorCount: 0,
|
|
129
|
+
},
|
|
130
|
+
semaphore: options.concurrency ? { running: 0, queue: [] } : undefined,
|
|
131
|
+
});
|
|
132
|
+
return this;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Execute a handler with concurrency control and metrics tracking
|
|
137
|
+
*/
|
|
138
|
+
private async executeHandler(route: Route, log: LogEntry): Promise<void> {
|
|
139
|
+
// Concurrency control via semaphore
|
|
140
|
+
if (route.semaphore && route.options.concurrency) {
|
|
141
|
+
if (route.semaphore.running >= route.options.concurrency) {
|
|
142
|
+
// Wait for slot to become available
|
|
143
|
+
await new Promise<void>((resolve) => {
|
|
144
|
+
route.semaphore!.queue.push(resolve);
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
route.semaphore.running++;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const startTime = route.options.trackMetrics ? performance.now() : 0;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const result = route.handler(log);
|
|
154
|
+
if (result instanceof Promise) {
|
|
155
|
+
await result;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Update metrics on success
|
|
159
|
+
if (route.options.trackMetrics) {
|
|
160
|
+
const duration = performance.now() - startTime;
|
|
161
|
+
route.metrics.count++;
|
|
162
|
+
route.metrics.totalDuration += duration;
|
|
163
|
+
route.metrics.minDuration = Math.min(
|
|
164
|
+
route.metrics.minDuration,
|
|
165
|
+
duration
|
|
166
|
+
);
|
|
167
|
+
route.metrics.maxDuration = Math.max(
|
|
168
|
+
route.metrics.maxDuration,
|
|
169
|
+
duration
|
|
170
|
+
);
|
|
171
|
+
route.metrics.lastExecuted = Date.now();
|
|
172
|
+
}
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// Track execution even on error
|
|
175
|
+
if (route.options.trackMetrics) {
|
|
176
|
+
const duration = performance.now() - startTime;
|
|
177
|
+
route.metrics.count++;
|
|
178
|
+
route.metrics.totalDuration += duration;
|
|
179
|
+
route.metrics.minDuration = Math.min(
|
|
180
|
+
route.metrics.minDuration,
|
|
181
|
+
duration
|
|
182
|
+
);
|
|
183
|
+
route.metrics.maxDuration = Math.max(
|
|
184
|
+
route.metrics.maxDuration,
|
|
185
|
+
duration
|
|
186
|
+
);
|
|
187
|
+
route.metrics.lastExecuted = Date.now();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Update error count
|
|
191
|
+
route.metrics.errorCount++;
|
|
192
|
+
|
|
193
|
+
console.error(
|
|
194
|
+
`Topic router error in route ${route.name || route.pattern}:`,
|
|
195
|
+
error
|
|
196
|
+
);
|
|
197
|
+
} finally {
|
|
198
|
+
// Release semaphore slot
|
|
199
|
+
if (route.semaphore) {
|
|
200
|
+
route.semaphore.running--;
|
|
201
|
+
const nextInQueue = route.semaphore.queue.shift();
|
|
202
|
+
if (nextInQueue) {
|
|
203
|
+
nextInQueue();
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Route a single log entry to matching handlers
|
|
211
|
+
* @param log - Log entry to route
|
|
212
|
+
*/
|
|
213
|
+
async route(log: LogEntry): Promise<void> {
|
|
214
|
+
if (!log.topic) {
|
|
215
|
+
// Skip logs without topics (backwards compatibility)
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Default priority to 1 if not specified
|
|
220
|
+
const logPriority = log.priority ?? 1;
|
|
221
|
+
|
|
222
|
+
const promises: Promise<void>[] = [];
|
|
223
|
+
|
|
224
|
+
for (const route of this.routes) {
|
|
225
|
+
// Check topic pattern match
|
|
226
|
+
if (!route.pattern.test(log.topic)) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Check priority range if specified
|
|
231
|
+
const minPriority = route.options.minPriority ?? 1;
|
|
232
|
+
const maxPriority = route.options.maxPriority ?? 10;
|
|
233
|
+
|
|
234
|
+
if (logPriority < minPriority || logPriority > maxPriority) {
|
|
235
|
+
continue; // Skip this route - priority out of range
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
promises.push(this.executeHandler(route, log));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Wait for all handlers to complete
|
|
242
|
+
if (promises.length > 0) {
|
|
243
|
+
await Promise.all(promises);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Route a batch of log entries in parallel
|
|
249
|
+
* @param logs - Array of log entries to route
|
|
250
|
+
*/
|
|
251
|
+
async routeBatch(logs: LogEntry[]): Promise<void> {
|
|
252
|
+
await Promise.all(logs.map((log) => this.route(log)));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Clear all routes
|
|
257
|
+
*/
|
|
258
|
+
clearRoutes(): void {
|
|
259
|
+
this.routes = [];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Get all registered routes
|
|
264
|
+
*/
|
|
265
|
+
getRoutes(): Route[] {
|
|
266
|
+
return [...this.routes];
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Get metrics for all routes
|
|
271
|
+
*/
|
|
272
|
+
getMetrics(): RouteMetrics[] {
|
|
273
|
+
return this.routes
|
|
274
|
+
.filter((route) => route.options.trackMetrics)
|
|
275
|
+
.map((route) => ({
|
|
276
|
+
name: route.name || "unnamed",
|
|
277
|
+
pattern: route.pattern.source,
|
|
278
|
+
executionCount: route.metrics.count,
|
|
279
|
+
totalDuration: route.metrics.totalDuration,
|
|
280
|
+
averageDuration:
|
|
281
|
+
route.metrics.count > 0
|
|
282
|
+
? route.metrics.totalDuration / route.metrics.count
|
|
283
|
+
: 0,
|
|
284
|
+
minDuration:
|
|
285
|
+
route.metrics.minDuration === Infinity
|
|
286
|
+
? 0
|
|
287
|
+
: route.metrics.minDuration,
|
|
288
|
+
maxDuration: route.metrics.maxDuration,
|
|
289
|
+
errorCount: route.metrics.errorCount,
|
|
290
|
+
lastExecuted: route.metrics.lastExecuted,
|
|
291
|
+
}));
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Reset all metrics
|
|
296
|
+
*/
|
|
297
|
+
resetMetrics(): void {
|
|
298
|
+
for (const route of this.routes) {
|
|
299
|
+
route.metrics.count = 0;
|
|
300
|
+
route.metrics.totalDuration = 0;
|
|
301
|
+
route.metrics.minDuration = Infinity;
|
|
302
|
+
route.metrics.maxDuration = 0;
|
|
303
|
+
route.metrics.errorCount = 0;
|
|
304
|
+
route.metrics.lastExecuted = undefined;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Convenience builder for common routing patterns
|
|
311
|
+
*/
|
|
312
|
+
export class TopicRouterBuilder {
|
|
313
|
+
private router = new TopicRouter();
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Route errors to alerting system (PagerDuty, Slack, etc.)
|
|
317
|
+
* @param handler - Handler function
|
|
318
|
+
* @param options - Route options (concurrency, metrics tracking)
|
|
319
|
+
*/
|
|
320
|
+
errors(handler: RouteHandler, options?: RouteOptions): this {
|
|
321
|
+
this.router.addRoute(
|
|
322
|
+
/^pipeline\..*\.error$|^pipeline\.error$/,
|
|
323
|
+
handler,
|
|
324
|
+
options ? { ...options } : "errors"
|
|
325
|
+
);
|
|
326
|
+
return this;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Route performance metrics to monitoring system (Prometheus, Datadog, etc.)
|
|
331
|
+
* @param handler - Handler function
|
|
332
|
+
* @param options - Route options (concurrency, metrics tracking)
|
|
333
|
+
*/
|
|
334
|
+
performance(handler: RouteHandler, options?: RouteOptions): this {
|
|
335
|
+
this.router.addRoute(
|
|
336
|
+
/^pipeline\..*\.performance$/,
|
|
337
|
+
handler,
|
|
338
|
+
options ? { ...options } : "performance"
|
|
339
|
+
);
|
|
340
|
+
return this;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Route stage-specific logs
|
|
345
|
+
* @param stageName - Name of the stage
|
|
346
|
+
* @param handler - Handler function
|
|
347
|
+
* @param options - Route options (concurrency, metrics tracking)
|
|
348
|
+
*/
|
|
349
|
+
stage(
|
|
350
|
+
stageName: string,
|
|
351
|
+
handler: RouteHandler,
|
|
352
|
+
options?: RouteOptions
|
|
353
|
+
): this {
|
|
354
|
+
this.router.addRoute(
|
|
355
|
+
new RegExp("^pipeline\\.stage\\." + stageName + "\\."),
|
|
356
|
+
handler,
|
|
357
|
+
options ? { ...options } : `stage-${stageName}`
|
|
358
|
+
);
|
|
359
|
+
return this;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Route alerts (threshold crossings, anomalies)
|
|
364
|
+
* @param handler - Handler function
|
|
365
|
+
* @param options - Route options (concurrency, metrics tracking)
|
|
366
|
+
*/
|
|
367
|
+
alerts(handler: RouteHandler, options?: RouteOptions): this {
|
|
368
|
+
this.router.addRoute(
|
|
369
|
+
/^pipeline\.alert/,
|
|
370
|
+
handler,
|
|
371
|
+
options ? { ...options } : "alerts"
|
|
372
|
+
);
|
|
373
|
+
return this;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Route debug logs to centralized logging (Loki, Elasticsearch, etc.)
|
|
378
|
+
* @param handler - Handler function
|
|
379
|
+
* @param options - Route options (concurrency, metrics tracking)
|
|
380
|
+
*/
|
|
381
|
+
debug(handler: RouteHandler, options?: RouteOptions): this {
|
|
382
|
+
this.router.addRoute(
|
|
383
|
+
/^pipeline\.debug/,
|
|
384
|
+
handler,
|
|
385
|
+
options ? { ...options } : "debug"
|
|
386
|
+
);
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Catch-all route (should be added last)
|
|
392
|
+
* @param handler - Handler function
|
|
393
|
+
* @param options - Route options (concurrency, metrics tracking)
|
|
394
|
+
*/
|
|
395
|
+
default(handler: RouteHandler, options?: RouteOptions): this {
|
|
396
|
+
this.router.addRoute(/.*/, handler, options ? { ...options } : "default");
|
|
397
|
+
return this;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* Custom route with pattern
|
|
402
|
+
* @param pattern - Regex or string pattern
|
|
403
|
+
* @param handler - Handler function
|
|
404
|
+
* @param nameOrOptions - Optional name or route options
|
|
405
|
+
*/
|
|
406
|
+
custom(
|
|
407
|
+
pattern: RegExp | string,
|
|
408
|
+
handler: RouteHandler,
|
|
409
|
+
nameOrOptions?: string | RouteOptions
|
|
410
|
+
): this {
|
|
411
|
+
this.router.addRoute(pattern, handler, nameOrOptions);
|
|
412
|
+
return this;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Build and return the configured router
|
|
417
|
+
*/
|
|
418
|
+
build(): TopicRouter {
|
|
419
|
+
return this.router;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Create a new topic router builder
|
|
425
|
+
*/
|
|
426
|
+
export function createTopicRouter(): TopicRouterBuilder {
|
|
427
|
+
return new TopicRouterBuilder();
|
|
428
|
+
}
|