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,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Production Topic Router Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates fan-out routing to multiple observability backends:
|
|
5
|
+
* - Errors → PagerDuty/Alerting
|
|
6
|
+
* - Performance → Prometheus/Metrics
|
|
7
|
+
* - Debug → Loki/Centralized Logging
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { createDspPipeline, createTopicRouter } from "../../index.js";
|
|
11
|
+
import type { LogEntry } from "../../types.js";
|
|
12
|
+
|
|
13
|
+
console.log("Production Topic Router Example\n");
|
|
14
|
+
|
|
15
|
+
// Simulate observability backends
|
|
16
|
+
const backends = {
|
|
17
|
+
pagerDuty: {
|
|
18
|
+
alert: async (log: LogEntry) => {
|
|
19
|
+
console.log(` 🚨 [PagerDuty] ALERT: ${log.message}`);
|
|
20
|
+
console.log(` Topic: ${log.topic}`);
|
|
21
|
+
console.log(` Context:`, log.context);
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
prometheus: {
|
|
26
|
+
record: async (topic: string, context?: Record<string, any>) => {
|
|
27
|
+
console.log(` [Prometheus] Metric recorded`);
|
|
28
|
+
console.log(` Metric: ${topic}`);
|
|
29
|
+
console.log(` Labels:`, context);
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
loki: {
|
|
34
|
+
send: async (log: LogEntry) => {
|
|
35
|
+
console.log(` [Loki] Log ingested`);
|
|
36
|
+
console.log(` Level: ${log.level}`);
|
|
37
|
+
console.log(` Message: ${log.message}`);
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
cloudwatch: {
|
|
42
|
+
send: async (log: LogEntry) => {
|
|
43
|
+
console.log(` [CloudWatch] Log sent`);
|
|
44
|
+
console.log(` Stream: ${log.topic}`);
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
console.log("1. Builder Pattern (Recommended)\n");
|
|
50
|
+
{
|
|
51
|
+
const router = createTopicRouter()
|
|
52
|
+
.errors(backends.pagerDuty.alert)
|
|
53
|
+
.performance((log) => backends.prometheus.record(log.topic!, log.context))
|
|
54
|
+
.debug(backends.loki.send)
|
|
55
|
+
.default(backends.cloudwatch.send) // Catch-all
|
|
56
|
+
.build();
|
|
57
|
+
|
|
58
|
+
const pipeline = createDspPipeline();
|
|
59
|
+
pipeline
|
|
60
|
+
.pipeline({
|
|
61
|
+
onLogBatch: (logs) => router.routeBatch(logs),
|
|
62
|
+
})
|
|
63
|
+
.MovingAverage({ mode: "moving", windowSize: 3 })
|
|
64
|
+
.Rms({ mode: "moving", windowSize: 5 });
|
|
65
|
+
|
|
66
|
+
const samples = new Float32Array([1, -2, 3, -4, 5]);
|
|
67
|
+
await pipeline.process(samples, { sampleRate: 1000 });
|
|
68
|
+
|
|
69
|
+
console.log();
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log("2. Manual Route Configuration\n");
|
|
73
|
+
{
|
|
74
|
+
const router = createTopicRouter()
|
|
75
|
+
.custom(/^pipeline\.error/, backends.pagerDuty.alert, "error-alerts")
|
|
76
|
+
.custom(
|
|
77
|
+
/^pipeline\.stage\.rms/,
|
|
78
|
+
(log) => backends.prometheus.record(log.topic!, log.context),
|
|
79
|
+
"rms-metrics"
|
|
80
|
+
)
|
|
81
|
+
.custom(
|
|
82
|
+
/^pipeline\.performance/,
|
|
83
|
+
(log) => backends.prometheus.record(log.topic!, log.context),
|
|
84
|
+
"performance-metrics"
|
|
85
|
+
)
|
|
86
|
+
.custom(/.*/, backends.loki.send, "default-logs")
|
|
87
|
+
.build();
|
|
88
|
+
|
|
89
|
+
const pipeline = createDspPipeline();
|
|
90
|
+
pipeline
|
|
91
|
+
.pipeline({
|
|
92
|
+
onLogBatch: (logs) => router.routeBatch(logs),
|
|
93
|
+
})
|
|
94
|
+
.MovingAverage({ mode: "moving", windowSize: 3 });
|
|
95
|
+
|
|
96
|
+
const samples = new Float32Array([1, 2, 3]);
|
|
97
|
+
await pipeline.process(samples, { sampleRate: 1000 });
|
|
98
|
+
|
|
99
|
+
console.log();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
console.log("3. Multi-Backend Routing (Fan-Out)\n");
|
|
103
|
+
{
|
|
104
|
+
// Route errors to BOTH PagerDuty AND CloudWatch
|
|
105
|
+
const router = createTopicRouter()
|
|
106
|
+
.errors(async (log) => {
|
|
107
|
+
await backends.pagerDuty.alert(log);
|
|
108
|
+
await backends.cloudwatch.send(log);
|
|
109
|
+
})
|
|
110
|
+
.performance((log) => backends.prometheus.record(log.topic!, log.context))
|
|
111
|
+
.default(backends.loki.send)
|
|
112
|
+
.build();
|
|
113
|
+
|
|
114
|
+
const pipeline = createDspPipeline();
|
|
115
|
+
pipeline
|
|
116
|
+
.pipeline({
|
|
117
|
+
onLogBatch: (logs) => router.routeBatch(logs),
|
|
118
|
+
})
|
|
119
|
+
.MovingAverage({ mode: "moving", windowSize: 3 });
|
|
120
|
+
|
|
121
|
+
const samples = new Float32Array([1, 2, 3]);
|
|
122
|
+
await pipeline.process(samples, { sampleRate: 1000 });
|
|
123
|
+
|
|
124
|
+
console.log();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
console.log("4. Stage-Specific Routing\n");
|
|
128
|
+
{
|
|
129
|
+
const router = createTopicRouter()
|
|
130
|
+
.stage("rms", async (log) => {
|
|
131
|
+
console.log(` [RMS-Specific] ${log.message}`);
|
|
132
|
+
await backends.prometheus.record(log.topic!, log.context);
|
|
133
|
+
})
|
|
134
|
+
.stage("movingAverage", async (log) => {
|
|
135
|
+
console.log(` [MA-Specific] ${log.message}`);
|
|
136
|
+
await backends.loki.send(log);
|
|
137
|
+
})
|
|
138
|
+
.default(backends.cloudwatch.send)
|
|
139
|
+
.build();
|
|
140
|
+
|
|
141
|
+
const pipeline = createDspPipeline();
|
|
142
|
+
pipeline
|
|
143
|
+
.pipeline({
|
|
144
|
+
onLogBatch: (logs) => router.routeBatch(logs),
|
|
145
|
+
})
|
|
146
|
+
.MovingAverage({ mode: "moving", windowSize: 3 })
|
|
147
|
+
.Rms({ mode: "moving", windowSize: 5 });
|
|
148
|
+
|
|
149
|
+
const samples = new Float32Array([1, -2, 3, -4, 5]);
|
|
150
|
+
await pipeline.process(samples, { sampleRate: 1000 });
|
|
151
|
+
|
|
152
|
+
console.log();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
console.log("5. Real-World Production Pattern\n");
|
|
156
|
+
{
|
|
157
|
+
console.log(" Routing Configuration:");
|
|
158
|
+
console.log(" • Errors → PagerDuty (critical alerts)");
|
|
159
|
+
console.log(" • Performance → Prometheus (metrics)");
|
|
160
|
+
console.log(" • Debug → Loki (centralized logs)");
|
|
161
|
+
console.log(" • Everything → CloudWatch (backup/audit)\n");
|
|
162
|
+
|
|
163
|
+
const router = createTopicRouter()
|
|
164
|
+
// Critical: Alert on errors
|
|
165
|
+
.errors(async (log) => {
|
|
166
|
+
console.log(` 🚨 [CRITICAL] Alerting team via PagerDuty`);
|
|
167
|
+
await backends.pagerDuty.alert(log);
|
|
168
|
+
await backends.cloudwatch.send(log); // Backup to CloudWatch
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
// Metrics: Export to Prometheus
|
|
172
|
+
.performance(async (log) => {
|
|
173
|
+
await backends.prometheus.record(log.topic!, log.context);
|
|
174
|
+
await backends.cloudwatch.send(log);
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
// Debug: Send to Loki
|
|
178
|
+
.debug(async (log) => {
|
|
179
|
+
await backends.loki.send(log);
|
|
180
|
+
await backends.cloudwatch.send(log);
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
// Default: CloudWatch only
|
|
184
|
+
.default(backends.cloudwatch.send)
|
|
185
|
+
.build();
|
|
186
|
+
|
|
187
|
+
const pipeline = createDspPipeline();
|
|
188
|
+
pipeline
|
|
189
|
+
.pipeline({
|
|
190
|
+
onLogBatch: (logs) => router.routeBatch(logs),
|
|
191
|
+
})
|
|
192
|
+
.MovingAverage({ mode: "moving", windowSize: 10 })
|
|
193
|
+
.Rectify()
|
|
194
|
+
.Rms({ mode: "moving", windowSize: 5 });
|
|
195
|
+
|
|
196
|
+
const samples = new Float32Array(100).map(() => Math.random() * 10 - 5);
|
|
197
|
+
await pipeline.process(samples, { sampleRate: 48000 });
|
|
198
|
+
|
|
199
|
+
console.log("\n Logs routed to multiple backends in parallel");
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
console.log("\nKey Benefits:");
|
|
203
|
+
console.log(" - Fan-out: Single log → Multiple backends");
|
|
204
|
+
console.log(" - Non-blocking: All routes processed in parallel");
|
|
205
|
+
console.log(" - Extensible: Add new routes without changing pipeline code");
|
|
206
|
+
console.log(" - Production-safe: Matches Loki, OTEL, FluentBit patterns");
|
|
207
|
+
console.log(" - Type-safe: Full TypeScript support");
|
|
208
|
+
|
|
209
|
+
console.log("\nProduction Deployment:");
|
|
210
|
+
console.log(" • Add rate limiting (p-queue) for alert storms");
|
|
211
|
+
console.log(" • Add retry logic for failed backend calls");
|
|
212
|
+
console.log(" • Add buffering for network outages");
|
|
213
|
+
console.log(" • Add metrics on router performance");
|
|
214
|
+
console.log(" • Configure topic conventions for your org");
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Topic-Based Logging Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates Kafka-style topic-based log filtering and routing.
|
|
5
|
+
* Topics follow hierarchical structure: pipeline.stage.<name>.<category>
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createDspPipeline } from "../../bindings.js";
|
|
9
|
+
import type { LogEntry } from "../../types.js";
|
|
10
|
+
|
|
11
|
+
console.log("Topic-Based Logging Example\n");
|
|
12
|
+
|
|
13
|
+
// Example 1: Subscribe to all logs (no filter)
|
|
14
|
+
console.log("1. All Logs (no filter):");
|
|
15
|
+
{
|
|
16
|
+
const processor = createDspPipeline();
|
|
17
|
+
processor.pipeline({
|
|
18
|
+
onLogBatch: (logs: LogEntry[]) => {
|
|
19
|
+
logs.forEach((log: LogEntry) => {
|
|
20
|
+
console.log(
|
|
21
|
+
` [${log.topic}] ${log.level.toUpperCase()}: ${log.message}`
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
processor.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 5 });
|
|
28
|
+
|
|
29
|
+
const samples = new Float32Array([1, -2, 3, -4, 5]);
|
|
30
|
+
await processor.process(samples, { sampleRate: 1000 });
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console.log("\n2. Filter by Stage (pipeline.stage.rms.*):");
|
|
34
|
+
{
|
|
35
|
+
const processor = createDspPipeline();
|
|
36
|
+
processor.pipeline({
|
|
37
|
+
onLogBatch: (logs: LogEntry[]) => {
|
|
38
|
+
console.log(` Received ${logs.length} logs from RMS stage:`);
|
|
39
|
+
logs.forEach((log: LogEntry) => {
|
|
40
|
+
console.log(` [${log.topic}] ${log.message}`);
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
topicFilter: "pipeline.stage.rms.*", // Only RMS logs
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
processor.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 5 });
|
|
47
|
+
|
|
48
|
+
const samples = new Float32Array([1, -2, 3, -4, 5]);
|
|
49
|
+
await processor.process(samples, { sampleRate: 1000 });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
console.log("\n3. Filter by Category (Errors Only - *.error):");
|
|
53
|
+
{
|
|
54
|
+
const processor = createDspPipeline();
|
|
55
|
+
processor.pipeline({
|
|
56
|
+
onLogBatch: (logs: LogEntry[]) => {
|
|
57
|
+
if (logs.length > 0) {
|
|
58
|
+
console.log(` 🚨 Received ${logs.length} error logs:`);
|
|
59
|
+
logs.forEach((log: LogEntry) => {
|
|
60
|
+
console.log(` [${log.topic}] ${log.message}`);
|
|
61
|
+
});
|
|
62
|
+
} else {
|
|
63
|
+
console.log(" No errors detected");
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
topicFilter: "pipeline.*.error", // Only errors from any stage
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
processor.MovingAverage({ mode: "moving", windowSize: 3 });
|
|
70
|
+
const samples = new Float32Array([1, 2, 3, 4, 5]);
|
|
71
|
+
await processor.process(samples, { sampleRate: 1000 });
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log("\n4. Multiple Topic Filters (Errors + Performance):");
|
|
75
|
+
{
|
|
76
|
+
const processor = createDspPipeline();
|
|
77
|
+
processor.pipeline({
|
|
78
|
+
onLogBatch: (logs: LogEntry[]) => {
|
|
79
|
+
console.log(` Received ${logs.length} logs:`);
|
|
80
|
+
logs.forEach((log: LogEntry) => {
|
|
81
|
+
const icon = log.topic?.includes("error") ? "🚨" : "⏱️";
|
|
82
|
+
console.log(` ${icon} [${log.topic}] ${log.message}`);
|
|
83
|
+
});
|
|
84
|
+
},
|
|
85
|
+
topicFilter: [
|
|
86
|
+
"pipeline.*.error", // All errors
|
|
87
|
+
"pipeline.*.performance", // All performance metrics
|
|
88
|
+
],
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
processor.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 5 });
|
|
92
|
+
|
|
93
|
+
const samples = new Float32Array([1, -2, 3, -4, 5]);
|
|
94
|
+
await processor.process(samples, { sampleRate: 1000 });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log("\n5. Topic-Based Routing (Production Pattern):");
|
|
98
|
+
{
|
|
99
|
+
// Simulate different backends for different topics
|
|
100
|
+
const errorAlerts: any[] = [];
|
|
101
|
+
const metrics: any[] = [];
|
|
102
|
+
const debugLogs: any[] = [];
|
|
103
|
+
|
|
104
|
+
const processor = createDspPipeline();
|
|
105
|
+
processor.pipeline({
|
|
106
|
+
onLogBatch: (logs: LogEntry[]) => {
|
|
107
|
+
// Route logs to different backends based on topic
|
|
108
|
+
logs.forEach((log: LogEntry) => {
|
|
109
|
+
if (log.topic?.includes("error")) {
|
|
110
|
+
errorAlerts.push(log);
|
|
111
|
+
} else if (
|
|
112
|
+
log.topic?.includes("performance") ||
|
|
113
|
+
log.topic?.includes("samples")
|
|
114
|
+
) {
|
|
115
|
+
metrics.push(log);
|
|
116
|
+
} else {
|
|
117
|
+
debugLogs.push(log);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
console.log(` Routed logs:`);
|
|
122
|
+
console.log(` → Error alerts: ${errorAlerts.length}`);
|
|
123
|
+
console.log(` → Metrics: ${metrics.length}`);
|
|
124
|
+
console.log(` → Debug logs: ${debugLogs.length}`);
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
processor.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 5 });
|
|
129
|
+
|
|
130
|
+
const samples = new Float32Array([1, -2, 3, -4, 5]);
|
|
131
|
+
await processor.process(samples, { sampleRate: 1000 });
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log("\n6. Topic Structure Examples:");
|
|
135
|
+
console.log(" Topic Hierarchy:");
|
|
136
|
+
console.log(" ├── pipeline.debug (General debug logs)");
|
|
137
|
+
console.log(" ├── pipeline.info (General info logs)");
|
|
138
|
+
console.log(" ├── pipeline.warn (General warnings)");
|
|
139
|
+
console.log(" ├── pipeline.error (General errors)");
|
|
140
|
+
console.log(" └── pipeline.stage.<name>.* (Stage-specific logs)");
|
|
141
|
+
console.log(" ├── samples (Sample-level data)");
|
|
142
|
+
console.log(" ├── performance (Timing/metrics)");
|
|
143
|
+
console.log(" └── error (Stage errors)");
|
|
144
|
+
|
|
145
|
+
console.log("\n Topic Filter Patterns:");
|
|
146
|
+
console.log(" • 'pipeline.stage.*' → All stage logs");
|
|
147
|
+
console.log(" • 'pipeline.stage.rms.*' → Only RMS stage logs");
|
|
148
|
+
console.log(" • 'pipeline.*.error' → All errors (any stage)");
|
|
149
|
+
console.log(" • 'pipeline.*.performance' → All performance metrics");
|
|
150
|
+
console.log(
|
|
151
|
+
" • ['pipeline.error', 'pipeline.stage.*.error'] → Multiple patterns"
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
console.log("\nTopic-based logging complete!");
|
|
155
|
+
console.log("\nProduction Benefits:");
|
|
156
|
+
console.log(" • Selective subscription reduces processing overhead");
|
|
157
|
+
console.log(
|
|
158
|
+
" • Route different topics to different backends (Kafka, Loki, etc.)"
|
|
159
|
+
);
|
|
160
|
+
console.log(" • Filter at source instead of post-processing");
|
|
161
|
+
console.log(" • Aligns with industry telemetry standards");
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chained DSP Pipeline (MovingAverage → RMS → Rectify)
|
|
3
|
+
* with Redis state persistence.
|
|
4
|
+
*
|
|
5
|
+
* This verifies:
|
|
6
|
+
* - Multi-stage serialization to Redis
|
|
7
|
+
* - Restoration from Redis into a fresh pipeline
|
|
8
|
+
* - Stateless Rectify doesn’t corrupt persisted filters
|
|
9
|
+
* - Outputs match expected chaining results
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { createClient } from "redis";
|
|
13
|
+
import { createDspPipeline } from "../../bindings";
|
|
14
|
+
|
|
15
|
+
async function testChainingWithRedis(startFresh = false) {
|
|
16
|
+
console.log("=== DSP Chaining + Redis State Persistence Test ===\n");
|
|
17
|
+
|
|
18
|
+
// 1. Connect to Redis
|
|
19
|
+
const redis = await createClient({ url: "redis://localhost:6379" }).connect();
|
|
20
|
+
const stateKey = "dsp:pipeline:chained";
|
|
21
|
+
|
|
22
|
+
if (startFresh) {
|
|
23
|
+
console.log("Clearing previous Redis state...");
|
|
24
|
+
await redis.del(stateKey);
|
|
25
|
+
console.log("Redis state cleared.\n");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2. Create a chained pipeline
|
|
29
|
+
const pipeline = createDspPipeline({
|
|
30
|
+
redisHost: "localhost",
|
|
31
|
+
redisPort: 6379,
|
|
32
|
+
stateKey,
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
pipeline.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 3 }).Rectify();
|
|
36
|
+
|
|
37
|
+
console.log("Pipeline: MovingAverage → RMS → Rectify\n");
|
|
38
|
+
|
|
39
|
+
// 3️. Attempt to restore previous state
|
|
40
|
+
const previousState = await redis.get(stateKey);
|
|
41
|
+
if (previousState) {
|
|
42
|
+
console.log("Found existing state in Redis. Restoring...\n");
|
|
43
|
+
await pipeline.loadState(previousState);
|
|
44
|
+
console.log("State restored successfully!\n");
|
|
45
|
+
} else {
|
|
46
|
+
console.log("No previous state found — starting fresh.\n");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// 4. Process first batch
|
|
50
|
+
const batch1 = new Float32Array([1, -2, 3, -4, 5, -6]);
|
|
51
|
+
console.log("Input batch 1:", Array.from(batch1));
|
|
52
|
+
|
|
53
|
+
const output1 = await pipeline.processCopy(batch1, {
|
|
54
|
+
sampleRate: 1000,
|
|
55
|
+
});
|
|
56
|
+
console.log(
|
|
57
|
+
"Output batch 1:",
|
|
58
|
+
Array.from(output1).map((v) => v.toFixed(4))
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
// 5. Save state to Redis
|
|
62
|
+
const state1 = await pipeline.saveState();
|
|
63
|
+
await redis.set(stateKey, state1);
|
|
64
|
+
console.log("\nState saved to Redis:");
|
|
65
|
+
console.log(JSON.stringify(JSON.parse(state1), null, 2));
|
|
66
|
+
|
|
67
|
+
// 6. Simulate process restart
|
|
68
|
+
console.log("\n--- Simulating Restart ---\n");
|
|
69
|
+
const pipeline2 = createDspPipeline({
|
|
70
|
+
redisHost: "localhost",
|
|
71
|
+
redisPort: 6379,
|
|
72
|
+
stateKey,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
pipeline2.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 3 }).Rectify();
|
|
76
|
+
|
|
77
|
+
const restoredState = await redis.get(stateKey);
|
|
78
|
+
if (restoredState) {
|
|
79
|
+
console.log("Restoring pipeline from Redis...");
|
|
80
|
+
await pipeline2.loadState(restoredState);
|
|
81
|
+
console.log("Pipeline state fully restored.\n");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 7. Process second batch (continuation test)
|
|
85
|
+
const batch2 = new Float32Array([7, -8, 9]);
|
|
86
|
+
console.log("Input batch 2:", Array.from(batch2));
|
|
87
|
+
|
|
88
|
+
const output2 = await pipeline2.processCopy(batch2, {
|
|
89
|
+
sampleRate: 1000,
|
|
90
|
+
});
|
|
91
|
+
console.log(
|
|
92
|
+
"Output batch 2:",
|
|
93
|
+
Array.from(output2).map((v) => v.toFixed(4))
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
// 8. Inspect rectifier behavior (stateless)
|
|
97
|
+
const savedState2 = await pipeline2.saveState();
|
|
98
|
+
const parsed = JSON.parse(savedState2);
|
|
99
|
+
const rectifyState = parsed.stages.find((s: any) => s.type === "rectify");
|
|
100
|
+
|
|
101
|
+
console.log("\nRectify stage state:");
|
|
102
|
+
console.log(rectifyState || "Rectify stage not found in saved state!");
|
|
103
|
+
|
|
104
|
+
console.log("\nChaining + Redis test complete!");
|
|
105
|
+
|
|
106
|
+
// 9. Disconnect from Redis
|
|
107
|
+
await redis.disconnect();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Toggle this to reset Redis between runs
|
|
111
|
+
const START_FRESH = false;
|
|
112
|
+
|
|
113
|
+
testChainingWithRedis(START_FRESH).catch(console.error);
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// src/ts/examples/test-chaining.ts
|
|
2
|
+
import { createDspPipeline } from "../../bindings";
|
|
3
|
+
|
|
4
|
+
async function testChaining() {
|
|
5
|
+
console.log("=== Testing DSP Stage Chaining ===\n");
|
|
6
|
+
|
|
7
|
+
const pipeline = createDspPipeline();
|
|
8
|
+
pipeline.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 3 }).Rectify();
|
|
9
|
+
|
|
10
|
+
console.log("Pipeline: MovingAverage → RMS → Rectify");
|
|
11
|
+
|
|
12
|
+
const input = new Float32Array([1, -2, 3, -4, 5, -6]);
|
|
13
|
+
console.log("Input:", Array.from(input));
|
|
14
|
+
|
|
15
|
+
// sampleRate REQUIRED, channels optional
|
|
16
|
+
const output = await pipeline.processCopy(input, {
|
|
17
|
+
sampleRate: 1000, // required
|
|
18
|
+
// channels: 1 // optional; defaults to 1
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
console.log(
|
|
22
|
+
"Output:",
|
|
23
|
+
Array.from(output).map((v) => v.toFixed(4))
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const state = await pipeline.saveState();
|
|
27
|
+
console.log("\n--- Saved State ---");
|
|
28
|
+
console.log(JSON.stringify(JSON.parse(state), null, 2));
|
|
29
|
+
|
|
30
|
+
// Simulate restart
|
|
31
|
+
const pipeline2 = createDspPipeline();
|
|
32
|
+
pipeline2.MovingAverage({ mode: "moving", windowSize: 3 }).Rms({ mode: "moving", windowSize: 3 }).Rectify();
|
|
33
|
+
|
|
34
|
+
await pipeline2.loadState(state);
|
|
35
|
+
console.log("\nState restored.");
|
|
36
|
+
|
|
37
|
+
const nextInput = new Float32Array([7, -8, 9]);
|
|
38
|
+
const nextOutput = await pipeline2.processCopy(nextInput, {
|
|
39
|
+
sampleRate: 1000, // required
|
|
40
|
+
// channels: 1
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
console.log("Next input:", Array.from(nextInput));
|
|
44
|
+
console.log(
|
|
45
|
+
"Next output:",
|
|
46
|
+
Array.from(nextOutput).map((v) => v.toFixed(4))
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
console.log("\nChaining test complete!");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
testChaining().catch(console.error);
|