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.
Files changed (172) hide show
  1. package/.github/workflows/ci.yml +185 -0
  2. package/.vscode/c_cpp_properties.json +17 -0
  3. package/.vscode/settings.json +68 -0
  4. package/.vscode/tasks.json +28 -0
  5. package/DISCLAIMER.md +32 -0
  6. package/LICENSE +21 -0
  7. package/README.md +1803 -0
  8. package/ROADMAP.md +192 -0
  9. package/TECHNICAL_DEBT.md +165 -0
  10. package/binding.gyp +65 -0
  11. package/docs/ADVANCED_LOGGER_FEATURES.md +598 -0
  12. package/docs/AUTHENTICATION_SECURITY.md +396 -0
  13. package/docs/BACKEND_IMPROVEMENTS.md +399 -0
  14. package/docs/CHEBYSHEV_BIQUAD_EQ_IMPLEMENTATION.md +405 -0
  15. package/docs/FFT_IMPLEMENTATION.md +490 -0
  16. package/docs/FFT_IMPROVEMENTS_SUMMARY.md +387 -0
  17. package/docs/FFT_USER_GUIDE.md +494 -0
  18. package/docs/FILTERS_IMPLEMENTATION.md +260 -0
  19. package/docs/FILTER_API_GUIDE.md +418 -0
  20. package/docs/FIR_SIMD_OPTIMIZATION.md +175 -0
  21. package/docs/LOGGER_API_REFERENCE.md +350 -0
  22. package/docs/NOTCH_FILTER_QUICK_REF.md +121 -0
  23. package/docs/PHASE2_TESTS_AND_NOTCH_FILTER.md +341 -0
  24. package/docs/PHASES_5_7_SUMMARY.md +403 -0
  25. package/docs/PIPELINE_FILTER_INTEGRATION.md +446 -0
  26. package/docs/SIMD_OPTIMIZATIONS.md +211 -0
  27. package/docs/TEST_MIGRATION_SUMMARY.md +173 -0
  28. package/docs/TIMESERIES_IMPLEMENTATION_SUMMARY.md +322 -0
  29. package/docs/TIMESERIES_QUICK_REF.md +85 -0
  30. package/docs/advanced.md +559 -0
  31. package/docs/time-series-guide.md +617 -0
  32. package/docs/time-series-migration.md +376 -0
  33. package/jest.config.js +37 -0
  34. package/package.json +42 -0
  35. package/prebuilds/linux-x64/dsp-ts-redis.node +0 -0
  36. package/prebuilds/win32-x64/dsp-ts-redis.node +0 -0
  37. package/scripts/test.js +24 -0
  38. package/src/build/dsp-ts-redis.node +0 -0
  39. package/src/native/DspPipeline.cc +675 -0
  40. package/src/native/DspPipeline.h +44 -0
  41. package/src/native/FftBindings.cc +817 -0
  42. package/src/native/FilterBindings.cc +1001 -0
  43. package/src/native/IDspStage.h +53 -0
  44. package/src/native/adapters/InterpolatorStage.h +201 -0
  45. package/src/native/adapters/MeanAbsoluteValueStage.h +289 -0
  46. package/src/native/adapters/MovingAverageStage.h +306 -0
  47. package/src/native/adapters/RectifyStage.h +88 -0
  48. package/src/native/adapters/ResamplerStage.h +238 -0
  49. package/src/native/adapters/RmsStage.h +299 -0
  50. package/src/native/adapters/SscStage.h +121 -0
  51. package/src/native/adapters/VarianceStage.h +307 -0
  52. package/src/native/adapters/WampStage.h +114 -0
  53. package/src/native/adapters/WaveformLengthStage.h +115 -0
  54. package/src/native/adapters/ZScoreNormalizeStage.h +326 -0
  55. package/src/native/core/FftEngine.cc +441 -0
  56. package/src/native/core/FftEngine.h +224 -0
  57. package/src/native/core/FirFilter.cc +324 -0
  58. package/src/native/core/FirFilter.h +149 -0
  59. package/src/native/core/IirFilter.cc +576 -0
  60. package/src/native/core/IirFilter.h +210 -0
  61. package/src/native/core/MovingAbsoluteValueFilter.cc +17 -0
  62. package/src/native/core/MovingAbsoluteValueFilter.h +135 -0
  63. package/src/native/core/MovingAverageFilter.cc +18 -0
  64. package/src/native/core/MovingAverageFilter.h +135 -0
  65. package/src/native/core/MovingFftFilter.cc +291 -0
  66. package/src/native/core/MovingFftFilter.h +203 -0
  67. package/src/native/core/MovingVarianceFilter.cc +194 -0
  68. package/src/native/core/MovingVarianceFilter.h +114 -0
  69. package/src/native/core/MovingZScoreFilter.cc +215 -0
  70. package/src/native/core/MovingZScoreFilter.h +113 -0
  71. package/src/native/core/Policies.h +352 -0
  72. package/src/native/core/RmsFilter.cc +18 -0
  73. package/src/native/core/RmsFilter.h +131 -0
  74. package/src/native/core/SscFilter.cc +16 -0
  75. package/src/native/core/SscFilter.h +137 -0
  76. package/src/native/core/WampFilter.cc +16 -0
  77. package/src/native/core/WampFilter.h +101 -0
  78. package/src/native/core/WaveformLengthFilter.cc +17 -0
  79. package/src/native/core/WaveformLengthFilter.h +98 -0
  80. package/src/native/utils/CircularBufferArray.cc +336 -0
  81. package/src/native/utils/CircularBufferArray.h +62 -0
  82. package/src/native/utils/CircularBufferVector.cc +145 -0
  83. package/src/native/utils/CircularBufferVector.h +45 -0
  84. package/src/native/utils/NapiUtils.cc +53 -0
  85. package/src/native/utils/NapiUtils.h +21 -0
  86. package/src/native/utils/SimdOps.h +870 -0
  87. package/src/native/utils/SlidingWindowFilter.cc +239 -0
  88. package/src/native/utils/SlidingWindowFilter.h +159 -0
  89. package/src/native/utils/TimeSeriesBuffer.cc +205 -0
  90. package/src/native/utils/TimeSeriesBuffer.h +140 -0
  91. package/src/ts/CircularLogBuffer.ts +87 -0
  92. package/src/ts/DriftDetector.ts +331 -0
  93. package/src/ts/TopicRouter.ts +428 -0
  94. package/src/ts/__tests__/AdvancedDsp.test.ts +585 -0
  95. package/src/ts/__tests__/AuthAndEdgeCases.test.ts +241 -0
  96. package/src/ts/__tests__/Chaining.test.ts +387 -0
  97. package/src/ts/__tests__/ChebyshevBiquad.test.ts +229 -0
  98. package/src/ts/__tests__/CircularLogBuffer.test.ts +158 -0
  99. package/src/ts/__tests__/DriftDetector.test.ts +389 -0
  100. package/src/ts/__tests__/Fft.test.ts +484 -0
  101. package/src/ts/__tests__/ListState.test.ts +153 -0
  102. package/src/ts/__tests__/Logger.test.ts +208 -0
  103. package/src/ts/__tests__/LoggerAdvanced.test.ts +319 -0
  104. package/src/ts/__tests__/LoggerMinor.test.ts +247 -0
  105. package/src/ts/__tests__/MeanAbsoluteValue.test.ts +398 -0
  106. package/src/ts/__tests__/MovingAverage.test.ts +322 -0
  107. package/src/ts/__tests__/RMS.test.ts +315 -0
  108. package/src/ts/__tests__/Rectify.test.ts +272 -0
  109. package/src/ts/__tests__/Redis.test.ts +456 -0
  110. package/src/ts/__tests__/SlopeSignChange.test.ts +166 -0
  111. package/src/ts/__tests__/Tap.test.ts +164 -0
  112. package/src/ts/__tests__/TimeBasedExpiration.test.ts +124 -0
  113. package/src/ts/__tests__/TimeBasedRmsAndMav.test.ts +231 -0
  114. package/src/ts/__tests__/TimeBasedVarianceAndZScore.test.ts +284 -0
  115. package/src/ts/__tests__/TimeSeries.test.ts +254 -0
  116. package/src/ts/__tests__/TopicRouter.test.ts +332 -0
  117. package/src/ts/__tests__/TopicRouterAdvanced.test.ts +483 -0
  118. package/src/ts/__tests__/TopicRouterPriority.test.ts +487 -0
  119. package/src/ts/__tests__/Variance.test.ts +509 -0
  120. package/src/ts/__tests__/WaveformLength.test.ts +147 -0
  121. package/src/ts/__tests__/WillisonAmplitude.test.ts +197 -0
  122. package/src/ts/__tests__/ZScoreNormalize.test.ts +459 -0
  123. package/src/ts/advanced-dsp.ts +566 -0
  124. package/src/ts/backends.ts +1137 -0
  125. package/src/ts/bindings.ts +1225 -0
  126. package/src/ts/easter-egg.ts +42 -0
  127. package/src/ts/examples/MeanAbsoluteValue/test-state.ts +99 -0
  128. package/src/ts/examples/MeanAbsoluteValue/test-streaming.ts +269 -0
  129. package/src/ts/examples/MovingAverage/test-state.ts +85 -0
  130. package/src/ts/examples/MovingAverage/test-streaming.ts +188 -0
  131. package/src/ts/examples/RMS/test-state.ts +97 -0
  132. package/src/ts/examples/RMS/test-streaming.ts +253 -0
  133. package/src/ts/examples/Rectify/test-state.ts +107 -0
  134. package/src/ts/examples/Rectify/test-streaming.ts +242 -0
  135. package/src/ts/examples/Variance/test-state.ts +195 -0
  136. package/src/ts/examples/Variance/test-streaming.ts +260 -0
  137. package/src/ts/examples/ZScoreNormalize/test-state.ts +277 -0
  138. package/src/ts/examples/ZScoreNormalize/test-streaming.ts +306 -0
  139. package/src/ts/examples/advanced-dsp-examples.ts +397 -0
  140. package/src/ts/examples/callbacks/advanced-router-features.ts +326 -0
  141. package/src/ts/examples/callbacks/benchmark-circular-buffer.ts +109 -0
  142. package/src/ts/examples/callbacks/monitoring-example.ts +265 -0
  143. package/src/ts/examples/callbacks/pipeline-callbacks-example.ts +137 -0
  144. package/src/ts/examples/callbacks/pooled-callbacks-example.ts +274 -0
  145. package/src/ts/examples/callbacks/priority-routing-example.ts +277 -0
  146. package/src/ts/examples/callbacks/production-topic-router.ts +214 -0
  147. package/src/ts/examples/callbacks/topic-based-logging.ts +161 -0
  148. package/src/ts/examples/chaining/test-chaining-redis.ts +113 -0
  149. package/src/ts/examples/chaining/test-chaining.ts +52 -0
  150. package/src/ts/examples/emg-features-example.ts +284 -0
  151. package/src/ts/examples/fft-example.ts +309 -0
  152. package/src/ts/examples/fft-examples.ts +349 -0
  153. package/src/ts/examples/filter-examples.ts +320 -0
  154. package/src/ts/examples/list-state-example.ts +131 -0
  155. package/src/ts/examples/logger-example.ts +91 -0
  156. package/src/ts/examples/notch-filter-examples.ts +243 -0
  157. package/src/ts/examples/phase5/drift-detection-example.ts +290 -0
  158. package/src/ts/examples/phase6-7/production-observability.ts +476 -0
  159. package/src/ts/examples/phase6-7/redis-timeseries-integration.ts +446 -0
  160. package/src/ts/examples/redis/redis-example.ts +202 -0
  161. package/src/ts/examples/redis-example.ts +202 -0
  162. package/src/ts/examples/simd-benchmark.ts +126 -0
  163. package/src/ts/examples/tap-debugging.ts +230 -0
  164. package/src/ts/examples/timeseries/comparison-example.ts +290 -0
  165. package/src/ts/examples/timeseries/iot-sensor-example.ts +143 -0
  166. package/src/ts/examples/timeseries/redis-streaming-example.ts +233 -0
  167. package/src/ts/examples/waveform-length-example.ts +139 -0
  168. package/src/ts/fft.ts +722 -0
  169. package/src/ts/filters.ts +1078 -0
  170. package/src/ts/index.ts +120 -0
  171. package/src/ts/types.ts +589 -0
  172. 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);