@winkjs/composer 0.0.2 → 0.4.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 (451) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +42 -103
  3. package/package.json +53 -29
  4. package/src/composer.js +75 -0
  5. package/src/core/batch/aoo-encoder.js +148 -0
  6. package/src/core/batch/index.js +12 -0
  7. package/src/core/batch/sliding-window.js +91 -0
  8. package/src/core/codec/index.js +137 -0
  9. package/src/core/emitter-manager/mqtt/constants.js +102 -0
  10. package/src/core/emitter-manager/mqtt/emitter.js +780 -0
  11. package/src/core/emitter-manager/mqtt/index.js +190 -0
  12. package/src/core/emitter-manager/mqtt/mqtt-store.js +538 -0
  13. package/src/core/emitter-manager/terminal/assert-columns.js +98 -0
  14. package/src/core/emitter-manager/terminal/formatters.js +160 -0
  15. package/src/core/emitter-manager/terminal/index.js +341 -0
  16. package/src/core/env-vars.js +245 -0
  17. package/src/core/mqtt-protocol.js +47 -0
  18. package/src/core/partition-manager/index.js +7 -0
  19. package/src/core/partition-manager/init.js +44 -0
  20. package/src/core/partition-manager/introspect.js +15 -0
  21. package/src/core/partition-manager/publish-to.js +6 -0
  22. package/src/core/partition-manager/recompute.js +5 -0
  23. package/src/core/partition-manager/reset.js +5 -0
  24. package/src/core/partition-manager/update.js +264 -0
  25. package/src/core/partition-manager/validate-flow.js +157 -0
  26. package/src/core/semantics/digest.js +128 -0
  27. package/src/core/semantics/index.js +83 -0
  28. package/src/core/semantics/loader.js +571 -0
  29. package/src/core/semantics/schemas/asset-class-schema.js +218 -0
  30. package/src/core/semantics/schemas/column-schema.js +651 -0
  31. package/src/core/semantics/schemas/enum-schema.js +153 -0
  32. package/src/core/semantics/schemas/index.js +51 -0
  33. package/src/core/semantics/warnings.js +141 -0
  34. package/src/core/shutdown-manager/index.js +289 -0
  35. package/src/core/source-manager/csv/index.js +171 -0
  36. package/src/core/source-manager/csv/start.js +456 -0
  37. package/src/core/source-manager/mqtt/client.js +406 -0
  38. package/src/core/source-manager/mqtt/constants.js +147 -0
  39. package/src/core/source-manager/mqtt/dedup.js +244 -0
  40. package/src/core/source-manager/mqtt/index.js +260 -0
  41. package/src/core/source-manager/mqtt/status.js +546 -0
  42. package/src/core/source-manager/test-harness/comparator.js +226 -0
  43. package/src/core/source-manager/test-harness/field-generator.js +85 -0
  44. package/src/core/source-manager/test-harness/fuzz.js +87 -0
  45. package/src/core/source-manager/test-harness/index.js +115 -0
  46. package/src/core/source-manager/test-harness/prng.js +57 -0
  47. package/src/core/source-manager/test-harness/start.js +232 -0
  48. package/src/core/source-manager/test-harness/validate.js +213 -0
  49. package/src/core/storage-manager/questdb/assert-columns.js +120 -0
  50. package/src/core/storage-manager/questdb/ensure-tables.js +187 -0
  51. package/src/core/storage-manager/questdb/index.js +1251 -0
  52. package/src/core/storage-manager/questdb/persist-plan.js +488 -0
  53. package/src/core/storage-manager/questdb/writers.js +142 -0
  54. package/src/core/test-utils/sealed-record.js +80 -0
  55. package/src/core/test-utils/soak-signature.js +149 -0
  56. package/src/core/test-utils/tcp-proxy.js +106 -0
  57. package/src/core/tunable/helpers.js +254 -0
  58. package/src/core/tunable/index.js +87 -0
  59. package/src/core/utils/console-formatter/index.js +207 -0
  60. package/src/core/utils/create-smart-store-as/index.js +98 -0
  61. package/src/core/utils/create-smart-store-as/sanitize-field.js +46 -0
  62. package/src/core/utils/create-smart-store-as/smart-truncate.js +93 -0
  63. package/src/core/utils/flow/index.js +5 -0
  64. package/src/core/utils/flow/make-collision-checker.js +23 -0
  65. package/src/core/utils/flow/to-kebab.js +20 -0
  66. package/src/core/utils/half-life/index.js +111 -0
  67. package/src/core/utils/node/disable.js +11 -0
  68. package/src/core/utils/node/enable.js +11 -0
  69. package/src/core/utils/node/execute-triggers.js +40 -0
  70. package/src/core/utils/node/index.js +10 -0
  71. package/src/core/utils/node/pause.js +11 -0
  72. package/src/core/utils/node/populate-predicate-inputs.js +14 -0
  73. package/src/core/utils/node/publish-nan.js +19 -0
  74. package/src/core/utils/node/reset-predicate-inputs.js +11 -0
  75. package/src/core/utils/node/unpause.js +11 -0
  76. package/src/core/utils/node/validate-spec.js +22 -0
  77. package/src/core/utils/options/resolve-field-keyed.js +115 -0
  78. package/src/core/utils/quantize/index.js +75 -0
  79. package/src/core/utils/stats/formulas.js +67 -0
  80. package/src/core/utils/stats/index.js +3 -0
  81. package/src/core/utils/validate/composer-validators.js +141 -0
  82. package/src/core/utils/validate/helpers.js +206 -0
  83. package/src/core/utils/validate/index.js +12 -0
  84. package/src/core/utils/validate/type-validators.js +538 -0
  85. package/src/core/utils/validate/validate.js +107 -0
  86. package/src/core/utils/validate/validators.js +177 -0
  87. package/src/core/wiring/assert-handle.js +57 -0
  88. package/src/core/wiring/assert-module.js +63 -0
  89. package/src/core/wiring/index.js +5 -0
  90. package/src/core/wiring/node-type-to-module.js +17 -0
  91. package/src/core/wiring/wire-emitters.js +256 -0
  92. package/src/core/wiring/wire-linear-graph.js +25 -0
  93. package/src/core/wiring/wire-node.js +157 -0
  94. package/src/core/wiring/wire-semantics.js +173 -0
  95. package/src/core/wiring/wire-storages.js +344 -0
  96. package/src/flow/check-output-collisions.js +93 -0
  97. package/src/flow/consts.js +9 -0
  98. package/src/flow/deep-clone-spec.js +64 -0
  99. package/src/flow/driver.js +171 -0
  100. package/src/flow/expand-single-input-array.js +76 -0
  101. package/src/flow/expand-template-spec.js +93 -0
  102. package/src/flow/flow-api-schemas.js +300 -0
  103. package/src/flow/flow.js +658 -0
  104. package/src/flow/for-each.js +225 -0
  105. package/src/flow/get-signature-pattern.js +31 -0
  106. package/src/flow/get-target-array.js +46 -0
  107. package/src/flow/inspect.js +67 -0
  108. package/src/flow/load-node-module.js +81 -0
  109. package/src/flow/make-node-method.js +112 -0
  110. package/src/flow/resolve-pick-by-field.js +77 -0
  111. package/src/flow/resolve-tunables.js +76 -0
  112. package/src/flow/run.js +392 -0
  113. package/src/flow/serialize.js +161 -0
  114. package/src/flow/signatures-schema.js +222 -0
  115. package/src/flow/spec-builder.js +65 -0
  116. package/src/flow/validate-triggers.js +250 -0
  117. package/src/flow/validate.js +261 -0
  118. package/src/nodes/accumulate/index.js +21 -0
  119. package/src/nodes/accumulate/init.js +48 -0
  120. package/src/nodes/accumulate/introspect.js +123 -0
  121. package/src/nodes/accumulate/publish-to.js +34 -0
  122. package/src/nodes/accumulate/recompute.js +27 -0
  123. package/src/nodes/accumulate/reset.js +25 -0
  124. package/src/nodes/accumulate/update.js +42 -0
  125. package/src/nodes/appraise/calibrate.js +64 -0
  126. package/src/nodes/appraise/decision.js +66 -0
  127. package/src/nodes/appraise/deviation.js +140 -0
  128. package/src/nodes/appraise/index.js +23 -0
  129. package/src/nodes/appraise/init.js +165 -0
  130. package/src/nodes/appraise/integrate.js +45 -0
  131. package/src/nodes/appraise/introspect.js +352 -0
  132. package/src/nodes/appraise/publish-to.js +93 -0
  133. package/src/nodes/appraise/receptor.js +69 -0
  134. package/src/nodes/appraise/recompute.js +79 -0
  135. package/src/nodes/appraise/reset.js +53 -0
  136. package/src/nodes/appraise/update.js +130 -0
  137. package/src/nodes/archive/csv/index.js +6 -0
  138. package/src/nodes/archive/csv/init.js +36 -0
  139. package/src/nodes/archive/csv/load.js +70 -0
  140. package/src/nodes/archive/csv/reset.js +14 -0
  141. package/src/nodes/archive/csv/simple-parser.js +216 -0
  142. package/src/nodes/archive/csv/test.js +173 -0
  143. package/src/nodes/archive/csv/update.js +63 -0
  144. package/src/nodes/butterworth-filter/index.js +18 -0
  145. package/src/nodes/butterworth-filter/init.js +184 -0
  146. package/src/nodes/butterworth-filter/introspect.js +228 -0
  147. package/src/nodes/butterworth-filter/publish-to.js +21 -0
  148. package/src/nodes/butterworth-filter/recompute.js +13 -0
  149. package/src/nodes/butterworth-filter/reset.js +30 -0
  150. package/src/nodes/butterworth-filter/update.js +38 -0
  151. package/src/nodes/categorize/index.js +19 -0
  152. package/src/nodes/categorize/init.js +58 -0
  153. package/src/nodes/categorize/introspect.js +177 -0
  154. package/src/nodes/categorize/publish-to.js +29 -0
  155. package/src/nodes/categorize/recompute.js +14 -0
  156. package/src/nodes/categorize/reset.js +16 -0
  157. package/src/nodes/categorize/update.js +53 -0
  158. package/src/nodes/controller/index.js +9 -0
  159. package/src/nodes/controller/init.js +55 -0
  160. package/src/nodes/controller/introspect.js +91 -0
  161. package/src/nodes/controller/publish-to.js +8 -0
  162. package/src/nodes/controller/recompute.js +9 -0
  163. package/src/nodes/controller/reset.js +17 -0
  164. package/src/nodes/controller/update.js +63 -0
  165. package/src/nodes/diff/index.js +19 -0
  166. package/src/nodes/diff/init.js +37 -0
  167. package/src/nodes/diff/introspect.js +132 -0
  168. package/src/nodes/diff/publish-to.js +21 -0
  169. package/src/nodes/diff/recompute.js +12 -0
  170. package/src/nodes/diff/reset.js +12 -0
  171. package/src/nodes/diff/update.js +37 -0
  172. package/src/nodes/digest-moments/formulas.js +12 -0
  173. package/src/nodes/digest-moments/index.js +14 -0
  174. package/src/nodes/digest-moments/init.js +67 -0
  175. package/src/nodes/digest-moments/introspect.js +154 -0
  176. package/src/nodes/digest-moments/publish-to.js +34 -0
  177. package/src/nodes/digest-moments/recompute.js +14 -0
  178. package/src/nodes/digest-moments/reset.js +14 -0
  179. package/src/nodes/digest-moments/update.js +120 -0
  180. package/src/nodes/dwell-time-tracker/index.js +14 -0
  181. package/src/nodes/dwell-time-tracker/init.js +59 -0
  182. package/src/nodes/dwell-time-tracker/introspect.js +137 -0
  183. package/src/nodes/dwell-time-tracker/publish-to.js +22 -0
  184. package/src/nodes/dwell-time-tracker/recompute.js +8 -0
  185. package/src/nodes/dwell-time-tracker/reset.js +23 -0
  186. package/src/nodes/dwell-time-tracker/update.js +103 -0
  187. package/src/nodes/emit-if/helpers.js +94 -0
  188. package/src/nodes/emit-if/index.js +16 -0
  189. package/src/nodes/emit-if/init.js +67 -0
  190. package/src/nodes/emit-if/introspect.js +102 -0
  191. package/src/nodes/emit-if/publish-to.js +13 -0
  192. package/src/nodes/emit-if/recompute.js +12 -0
  193. package/src/nodes/emit-if/reset.js +27 -0
  194. package/src/nodes/emit-if/update.js +93 -0
  195. package/src/nodes/es-correlation/index.js +19 -0
  196. package/src/nodes/es-correlation/init.js +73 -0
  197. package/src/nodes/es-correlation/introspect.js +176 -0
  198. package/src/nodes/es-correlation/publish-to.js +32 -0
  199. package/src/nodes/es-correlation/recompute.js +56 -0
  200. package/src/nodes/es-correlation/reset.js +27 -0
  201. package/src/nodes/es-correlation/update.js +110 -0
  202. package/src/nodes/es-mean/index.js +20 -0
  203. package/src/nodes/es-mean/init.js +58 -0
  204. package/src/nodes/es-mean/introspect.js +129 -0
  205. package/src/nodes/es-mean/publish-to.js +19 -0
  206. package/src/nodes/es-mean/recompute.js +16 -0
  207. package/src/nodes/es-mean/reset.js +24 -0
  208. package/src/nodes/es-mean/update.js +69 -0
  209. package/src/nodes/es-pairwise-correlation/index.js +19 -0
  210. package/src/nodes/es-pairwise-correlation/init.js +98 -0
  211. package/src/nodes/es-pairwise-correlation/introspect.js +187 -0
  212. package/src/nodes/es-pairwise-correlation/publish-to.js +31 -0
  213. package/src/nodes/es-pairwise-correlation/recompute.js +69 -0
  214. package/src/nodes/es-pairwise-correlation/reset.js +29 -0
  215. package/src/nodes/es-pairwise-correlation/update.js +89 -0
  216. package/src/nodes/es-stats/index.js +21 -0
  217. package/src/nodes/es-stats/init.js +105 -0
  218. package/src/nodes/es-stats/introspect.js +157 -0
  219. package/src/nodes/es-stats/publish-to.js +42 -0
  220. package/src/nodes/es-stats/recompute.js +74 -0
  221. package/src/nodes/es-stats/reset.js +49 -0
  222. package/src/nodes/es-stats/update-envelope.js +44 -0
  223. package/src/nodes/es-stats/update-welford.js +64 -0
  224. package/src/nodes/es-stats/update.js +59 -0
  225. package/src/nodes/index.js +83 -0
  226. package/src/nodes/invert-flag/index.js +20 -0
  227. package/src/nodes/invert-flag/init.js +36 -0
  228. package/src/nodes/invert-flag/introspect.js +115 -0
  229. package/src/nodes/invert-flag/publish-to.js +23 -0
  230. package/src/nodes/invert-flag/recompute.js +12 -0
  231. package/src/nodes/invert-flag/reset.js +12 -0
  232. package/src/nodes/invert-flag/update.js +32 -0
  233. package/src/nodes/kalman1d/index.js +20 -0
  234. package/src/nodes/kalman1d/init.js +103 -0
  235. package/src/nodes/kalman1d/introspect.js +250 -0
  236. package/src/nodes/kalman1d/publish-to.js +34 -0
  237. package/src/nodes/kalman1d/recompute.js +23 -0
  238. package/src/nodes/kalman1d/reset.js +29 -0
  239. package/src/nodes/kalman1d/update.js +144 -0
  240. package/src/nodes/kernel/index.js +18 -0
  241. package/src/nodes/kernel/init.js +66 -0
  242. package/src/nodes/kernel/introspect.js +163 -0
  243. package/src/nodes/kernel/presets.js +74 -0
  244. package/src/nodes/kernel/publish-to.js +25 -0
  245. package/src/nodes/kernel/recompute.js +14 -0
  246. package/src/nodes/kernel/reset.js +20 -0
  247. package/src/nodes/kernel/update.js +69 -0
  248. package/src/nodes/lag/index.js +21 -0
  249. package/src/nodes/lag/init.js +87 -0
  250. package/src/nodes/lag/introspect.js +202 -0
  251. package/src/nodes/lag/publish-to.js +64 -0
  252. package/src/nodes/lag/recompute.js +33 -0
  253. package/src/nodes/lag/reset.js +49 -0
  254. package/src/nodes/lag/update.js +133 -0
  255. package/src/nodes/median3/index.js +19 -0
  256. package/src/nodes/median3/init.js +40 -0
  257. package/src/nodes/median3/introspect.js +116 -0
  258. package/src/nodes/median3/publish-to.js +21 -0
  259. package/src/nodes/median3/recompute.js +12 -0
  260. package/src/nodes/median3/reset.js +15 -0
  261. package/src/nodes/median3/update.js +58 -0
  262. package/src/nodes/moments-digest/copy-from.js +12 -0
  263. package/src/nodes/moments-digest/flush.js +17 -0
  264. package/src/nodes/moments-digest/index.js +132 -0
  265. package/src/nodes/moments-digest/init.js +84 -0
  266. package/src/nodes/moments-digest/introspect.js +122 -0
  267. package/src/nodes/moments-digest/prelude.js +28 -0
  268. package/src/nodes/moments-digest/publish-to.js +31 -0
  269. package/src/nodes/moments-digest/recompute.js +9 -0
  270. package/src/nodes/moments-digest/reset.js +20 -0
  271. package/src/nodes/moments-digest/update.js +182 -0
  272. package/src/nodes/node-names.js +67 -0
  273. package/src/nodes/page-hinkley/index.js +20 -0
  274. package/src/nodes/page-hinkley/init.js +66 -0
  275. package/src/nodes/page-hinkley/introspect.js +193 -0
  276. package/src/nodes/page-hinkley/publish-to.js +28 -0
  277. package/src/nodes/page-hinkley/recompute.js +14 -0
  278. package/src/nodes/page-hinkley/reset.js +23 -0
  279. package/src/nodes/page-hinkley/update.js +105 -0
  280. package/src/nodes/pass-if/index.js +21 -0
  281. package/src/nodes/pass-if/init.js +37 -0
  282. package/src/nodes/pass-if/introspect.js +93 -0
  283. package/src/nodes/pass-if/publish-to.js +15 -0
  284. package/src/nodes/pass-if/recompute.js +15 -0
  285. package/src/nodes/pass-if/reset.js +18 -0
  286. package/src/nodes/pass-if/update.js +37 -0
  287. package/src/nodes/persist-if/helpers.js +111 -0
  288. package/src/nodes/persist-if/index.js +9 -0
  289. package/src/nodes/persist-if/init.js +98 -0
  290. package/src/nodes/persist-if/introspect.js +106 -0
  291. package/src/nodes/persist-if/publish-to.js +21 -0
  292. package/src/nodes/persist-if/recompute.js +20 -0
  293. package/src/nodes/persist-if/reset.js +35 -0
  294. package/src/nodes/persist-if/update.js +112 -0
  295. package/src/nodes/persistence-check/index.js +14 -0
  296. package/src/nodes/persistence-check/init.js +38 -0
  297. package/src/nodes/persistence-check/introspect.js +144 -0
  298. package/src/nodes/persistence-check/publish-to.js +11 -0
  299. package/src/nodes/persistence-check/recompute.js +7 -0
  300. package/src/nodes/persistence-check/reset.js +12 -0
  301. package/src/nodes/persistence-check/update.js +57 -0
  302. package/src/nodes/process-index/index.js +15 -0
  303. package/src/nodes/process-index/init.js +48 -0
  304. package/src/nodes/process-index/introspect.js +182 -0
  305. package/src/nodes/process-index/publish-to.js +29 -0
  306. package/src/nodes/process-index/recompute.js +8 -0
  307. package/src/nodes/process-index/reset.js +8 -0
  308. package/src/nodes/process-index/update.js +75 -0
  309. package/src/nodes/ratio/index.js +14 -0
  310. package/src/nodes/ratio/init.js +37 -0
  311. package/src/nodes/ratio/introspect.js +155 -0
  312. package/src/nodes/ratio/publish-to.js +15 -0
  313. package/src/nodes/ratio/recompute.js +7 -0
  314. package/src/nodes/ratio/reset.js +7 -0
  315. package/src/nodes/ratio/update.js +47 -0
  316. package/src/nodes/sanitize/index.js +82 -0
  317. package/src/nodes/sanitize/init.js +82 -0
  318. package/src/nodes/sanitize/introspect.js +194 -0
  319. package/src/nodes/sanitize/publish-to.js +15 -0
  320. package/src/nodes/sanitize/recompute.js +8 -0
  321. package/src/nodes/sanitize/reset.js +14 -0
  322. package/src/nodes/sanitize/update.js +102 -0
  323. package/src/nodes/spike-guard/index.js +14 -0
  324. package/src/nodes/spike-guard/init.js +47 -0
  325. package/src/nodes/spike-guard/introspect.js +130 -0
  326. package/src/nodes/spike-guard/publish-to.js +35 -0
  327. package/src/nodes/spike-guard/recompute.js +14 -0
  328. package/src/nodes/spike-guard/reset.js +19 -0
  329. package/src/nodes/spike-guard/update.js +89 -0
  330. package/src/nodes/sse-emitter/index.js +7 -0
  331. package/src/nodes/sse-emitter/init.js +105 -0
  332. package/src/nodes/sse-emitter/introspect.js +134 -0
  333. package/src/nodes/sse-emitter/publish-to.js +6 -0
  334. package/src/nodes/sse-emitter/recompute.js +16 -0
  335. package/src/nodes/sse-emitter/reset.js +19 -0
  336. package/src/nodes/sse-emitter/update.js +69 -0
  337. package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +36 -0
  338. package/src/nodes/sse-emitter/utils/build-message.js +31 -0
  339. package/src/nodes/sse-emitter/utils/connection-manager.js +45 -0
  340. package/src/nodes/sse-emitter/utils/endpoint-initializer.js +20 -0
  341. package/src/nodes/sse-emitter/utils/health-monitor.js +38 -0
  342. package/src/nodes/sse-emitter/utils/qos-handler.js +41 -0
  343. package/src/nodes/state-change-detector/helpers.js +35 -0
  344. package/src/nodes/state-change-detector/index.js +12 -0
  345. package/src/nodes/state-change-detector/init.js +56 -0
  346. package/src/nodes/state-change-detector/introspect.js +146 -0
  347. package/src/nodes/state-change-detector/publish-to.js +20 -0
  348. package/src/nodes/state-change-detector/recompute.js +6 -0
  349. package/src/nodes/state-change-detector/reset.js +20 -0
  350. package/src/nodes/state-change-detector/update.js +97 -0
  351. package/src/nodes/sw-stats/index.js +14 -0
  352. package/src/nodes/sw-stats/init.js +51 -0
  353. package/src/nodes/sw-stats/introspect.js +131 -0
  354. package/src/nodes/sw-stats/publish-to.js +45 -0
  355. package/src/nodes/sw-stats/recompute.js +33 -0
  356. package/src/nodes/sw-stats/reset.js +15 -0
  357. package/src/nodes/sw-stats/update.js +39 -0
  358. package/src/nodes/swing-watch/index.js +17 -0
  359. package/src/nodes/swing-watch/init.js +144 -0
  360. package/src/nodes/swing-watch/introspect.js +188 -0
  361. package/src/nodes/swing-watch/publish-to.js +53 -0
  362. package/src/nodes/swing-watch/recompute.js +22 -0
  363. package/src/nodes/swing-watch/reset.js +55 -0
  364. package/src/nodes/swing-watch/update.js +767 -0
  365. package/src/nodes/tally/index.js +15 -0
  366. package/src/nodes/tally/init.js +32 -0
  367. package/src/nodes/tally/introspect.js +116 -0
  368. package/src/nodes/tally/publish-to.js +28 -0
  369. package/src/nodes/tally/recompute.js +10 -0
  370. package/src/nodes/tally/reset.js +10 -0
  371. package/src/nodes/tally/update.js +44 -0
  372. package/src/nodes/threshold/index.js +14 -0
  373. package/src/nodes/threshold/init.js +121 -0
  374. package/src/nodes/threshold/introspect.js +192 -0
  375. package/src/nodes/threshold/publish-to.js +11 -0
  376. package/src/nodes/threshold/recompute.js +8 -0
  377. package/src/nodes/threshold/reset.js +14 -0
  378. package/src/nodes/threshold/update.js +54 -0
  379. package/src/nodes/transform/helpers.js +39 -0
  380. package/src/nodes/transform/index.js +15 -0
  381. package/src/nodes/transform/init.js +35 -0
  382. package/src/nodes/transform/introspect.js +118 -0
  383. package/src/nodes/transform/publish-to.js +23 -0
  384. package/src/nodes/transform/recompute.js +12 -0
  385. package/src/nodes/transform/reset.js +11 -0
  386. package/src/nodes/transform/update.js +32 -0
  387. package/src/nodes/trend/compute-confidence.js +112 -0
  388. package/src/nodes/trend/index.js +14 -0
  389. package/src/nodes/trend/init.js +81 -0
  390. package/src/nodes/trend/introspect.js +151 -0
  391. package/src/nodes/trend/publish-to.js +22 -0
  392. package/src/nodes/trend/recompute.js +15 -0
  393. package/src/nodes/trend/reset.js +26 -0
  394. package/src/nodes/trend/update.js +93 -0
  395. package/src/nodes/tw-stats/copy-from.js +16 -0
  396. package/src/nodes/tw-stats/flush.js +12 -0
  397. package/src/nodes/tw-stats/index.js +41 -0
  398. package/src/nodes/tw-stats/init.js +92 -0
  399. package/src/nodes/tw-stats/introspect.js +171 -0
  400. package/src/nodes/tw-stats/publish-to.js +108 -0
  401. package/src/nodes/tw-stats/recompute.js +9 -0
  402. package/src/nodes/tw-stats/reset.js +16 -0
  403. package/src/nodes/tw-stats/update.js +113 -0
  404. package/src/nodes/unbalance/index.js +15 -0
  405. package/src/nodes/unbalance/init.js +59 -0
  406. package/src/nodes/unbalance/introspect.js +167 -0
  407. package/src/nodes/unbalance/publish-to.js +52 -0
  408. package/src/nodes/unbalance/recompute.js +11 -0
  409. package/src/nodes/unbalance/reset.js +10 -0
  410. package/src/nodes/unbalance/update.js +83 -0
  411. package/src/nodes/vector-distance/index.js +14 -0
  412. package/src/nodes/vector-distance/init.js +54 -0
  413. package/src/nodes/vector-distance/introspect.js +149 -0
  414. package/src/nodes/vector-distance/publish-to.js +35 -0
  415. package/src/nodes/vector-distance/recompute.js +11 -0
  416. package/src/nodes/vector-distance/reset.js +20 -0
  417. package/src/nodes/vector-distance/update.js +139 -0
  418. package/src/nodes/winnow/index.js +12 -0
  419. package/src/nodes/winnow/init.js +82 -0
  420. package/src/nodes/winnow/introspect.js +253 -0
  421. package/src/nodes/winnow/publish-to.js +51 -0
  422. package/src/nodes/winnow/recompute.js +14 -0
  423. package/src/nodes/winnow/reset.js +34 -0
  424. package/src/nodes/winnow/update.js +203 -0
  425. package/src/tools/training/features.js +227 -0
  426. package/src/tools/training/index.js +21 -0
  427. package/src/tools/training/logistic.js +238 -0
  428. package/src/tools/training/metrics.js +126 -0
  429. package/src/tools/training/scale.js +239 -0
  430. package/src/tools/training/validate.js +103 -0
  431. package/src/windowing/count-sliding/create.js +16 -0
  432. package/src/windowing/count-sliding/get-reverse-chronological-index.js +19 -0
  433. package/src/windowing/count-sliding/index.js +6 -0
  434. package/src/windowing/count-sliding/is-not-full.js +5 -0
  435. package/src/windowing/count-sliding/push.js +15 -0
  436. package/src/windowing/count-sliding/reset.js +11 -0
  437. package/src/windowing/count-sliding/size.js +5 -0
  438. package/src/windowing/time-sliding/create.js +25 -0
  439. package/src/windowing/time-sliding/push.js +42 -0
  440. package/CODE_OF_CONDUCT.md +0 -46
  441. package/CONTRIBUTING.md +0 -132
  442. package/docs/.nojekyll +0 -0
  443. package/docs/CONTRIBUTING.md +0 -134
  444. package/docs/README.md +0 -132
  445. package/docs/_coverpage.md +0 -26
  446. package/docs/_media/bg.png +0 -0
  447. package/docs/_media/wink-logo.png +0 -0
  448. package/docs/_navbar.md +0 -14
  449. package/docs/_sidebar.md +0 -2
  450. package/docs/index.html +0 -44
  451. package/eslint.config.js +0 -283
@@ -0,0 +1,63 @@
1
+ // nodes/csv-source/update.js
2
+ const update = function ( state, msg ) {
3
+ // Source nodes are special - they generate messages
4
+ // The msg parameter here is typically a trigger/timer signal
5
+
6
+ if ( !state.isLoaded || state.isPaused ) {
7
+ return null; // Nothing to emit
8
+ }
9
+
10
+ if ( state.currentIndex >= state.data.length ) {
11
+ if ( state.loop ) {
12
+ state.currentIndex = 0; // Reset to beginning
13
+ } else {
14
+ state.isPaused = true; // Stop emitting
15
+ return null;
16
+ }
17
+ }
18
+
19
+ // Rate limiting for stream mode
20
+ if ( state.mode === 'stream' ) {
21
+ const now = Date.now();
22
+ const timeSinceLastEmit = now - state.lastEmitTime;
23
+ const minInterval = 1000 / state.rate;
24
+
25
+ if ( timeSinceLastEmit < minInterval ) {
26
+ return null; // Too soon, skip this update
27
+ }
28
+
29
+ state.lastEmitTime = now;
30
+ }
31
+
32
+ // Get next row
33
+ const row = state.data[ state.currentIndex ];
34
+ state.currentIndex += 1;
35
+
36
+ // Create output message
37
+ const output = Object.create( null );
38
+
39
+ // Add metadata
40
+ output._source = {
41
+ type: 'csv',
42
+ index: state.currentIndex - 1,
43
+ total: state.data.length,
44
+ timestamp: Date.now()
45
+ };
46
+
47
+ // Map columns
48
+ if ( state.columns === 'auto' ) {
49
+ // Use all columns as-is
50
+ Object.assign( output, row );
51
+ } else {
52
+ // Custom column mapping
53
+ for ( const [ csvCol, msgField ] of Object.entries( state.columns ) ) {
54
+ if ( row.hasOwnProperty( csvCol ) ) {
55
+ output[ msgField ] = row[ csvCol ];
56
+ }
57
+ }
58
+ }
59
+
60
+ return output; // This will be the new message
61
+ }; // update()
62
+
63
+ export default update;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @fileoverview Public API barrel for butterworth-filter node.
3
+ *
4
+ * Re-exports all standard node interface functions and introspection metadata.
5
+ */
6
+
7
+ export { default as init } from './init.js';
8
+ export { default as update } from './update.js';
9
+ export { default as reset } from './reset.js';
10
+ export { default as recompute } from './recompute.js';
11
+ export { default as publishTo } from './publish-to.js';
12
+ // Direct imports for common control functions
13
+ export { default as disable } from '../../core/utils/node/disable.js';
14
+ export { default as enable } from '../../core/utils/node/enable.js';
15
+ export { default as pause } from '../../core/utils/node/pause.js';
16
+ export { default as unpause } from '../../core/utils/node/unpause.js';
17
+ // Re-export everything from introspection.js
18
+ export * from './introspect.js';
@@ -0,0 +1,184 @@
1
+ /**
2
+ * @fileoverview Initialization for butterworth-filter node.
3
+ *
4
+ * Validates the spec, computes 2nd-order Butterworth coefficients via the
5
+ * bilinear transform with frequency pre-warping, and returns fully
6
+ * initialized state. Supports lowpass/highpass, three cutoff specification
7
+ * methods, cascade adjustment, and DC initialization for transient reduction.
8
+ */
9
+
10
+ import { validateSpec } from '../../core/utils/node/index.js';
11
+ import * as introspect from './introspect.js';
12
+ import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
13
+
14
+ const init = function ( spec ) {
15
+ validateSpec( spec, introspect );
16
+
17
+ // Create state after validation passes
18
+ const state = Object.create( null );
19
+ // Standard input parameter health flag during update: false means healthy
20
+ state.inputValidationFailed = false;
21
+ // Ensure node is enabled by default
22
+ state.disable = false;
23
+ state.pause = false;
24
+
25
+ // Extract field name
26
+ state.x = spec.from.x;
27
+
28
+ // Apply defaults from introspect (validation doesn't enforce them)
29
+ // Supports both direct and field-keyed specification for tunable params
30
+ const filterTypeSpec = resolveScalar( spec.filterType, state.x );
31
+ state.filterType = filterTypeSpec ?? introspect.DEFAULT_OPTIONS.filterType;
32
+ state.acceptNumericalRisk = spec.acceptNumericalRisk ?? introspect.DEFAULT_OPTIONS.acceptNumericalRisk;
33
+
34
+ // Extract sample rate (required field, validated by schema)
35
+ const sampleRateHz = spec.sampleRateHz;
36
+ const nyquistHz = sampleRateHz / 2;
37
+
38
+ // Resolve tunable params (supports field-keying)
39
+ const cutoffHzSpec = resolveScalar( spec.cutoffHz, state.x );
40
+ const settlingTimeMsSpec = resolveScalar( spec.settlingTimeMs, state.x );
41
+ const cutoffRatioSpec = resolveScalar( spec.cutoffRatio, state.x );
42
+
43
+ // Determine cutoff frequency from various possible inputs
44
+ let cutoffHz;
45
+ let configIntent = 'direct';
46
+
47
+ if ( cutoffHzSpec ) {
48
+ // Direct specification
49
+ cutoffHz = cutoffHzSpec;
50
+
51
+ } else if ( settlingTimeMsSpec && spec.sampleRateHz ) {
52
+ // Calculate from settling time requirement
53
+ // For 2nd order Butterworth: settling time ≈ 4 / (2π * fc) for 98% settling
54
+ const settlingTimeSec = settlingTimeMsSpec / 1000;
55
+ cutoffHz = 4 / ( 2 * Math.PI * settlingTimeSec );
56
+ configIntent = 'settling-time';
57
+
58
+ } else if ( cutoffRatioSpec ) {
59
+ // Relative to Nyquist
60
+ cutoffHz = cutoffRatioSpec * nyquistHz;
61
+ configIntent = 'ratio';
62
+ }
63
+
64
+ // Adjust for cascading if specified
65
+ let cascadeAdjustment = 1;
66
+ if ( spec.adjustForCascade && spec.adjustForCascade > 1 ) {
67
+ // For N cascaded stages to have -3dB at fc:
68
+ // Each stage needs fc * 2^(1/N - 1)
69
+ cascadeAdjustment = Math.pow( 2, ( 1 / spec.adjustForCascade ) - 1 );
70
+ cutoffHz /= cascadeAdjustment;
71
+ configIntent += '-cascade-adjusted';
72
+ }
73
+
74
+ // Validate the computed cutoff
75
+ if ( cutoffHz <= 0 || cutoffHz >= nyquistHz ) {
76
+ throw new RangeError(
77
+ `Computed cutoff ${cutoffHz.toFixed(1)}Hz is outside valid range (0, ${nyquistHz}Hz)`
78
+ );
79
+ }
80
+
81
+ // Warn about potential numerical issues
82
+ const normalizedCutoff = cutoffHz / nyquistHz;
83
+ if ( normalizedCutoff < 0.001 && !state.acceptNumericalRisk ) {
84
+ console.warn( `Very low cutoff (${cutoffHz.toFixed(3)}Hz) may cause numerical instability` );
85
+ }
86
+
87
+ // Store configuration for reference
88
+ state.config = {
89
+ cutoffHz,
90
+ sampleRateHz,
91
+ normalizedCutoff,
92
+ intent: configIntent,
93
+ cascadeAdjustment,
94
+ dcEstimate: spec.dcEstimate // Store for reset functionality
95
+ };
96
+
97
+ // Calculate filter coefficients using bilinear transform
98
+ // Pre-warp the frequency to compensate for bilinear transform warping
99
+ const wc = ( Math.PI * normalizedCutoff ) / 2; // Bilinear pre-warp: tan(π·fc/fs)
100
+ const warpedWc = Math.tan( wc ); // Pre-warped cutoff frequency
101
+
102
+ // Butterworth prototype has Q = 1/√2 for maximally flat response
103
+ const Q = 1 / Math.sqrt( 2 );
104
+
105
+ // Bilinear transform coefficients
106
+ const K = warpedWc;
107
+ const K2 = K * K;
108
+ const norm = 1 / ( K2 + ( K / Q ) + 1 );
109
+
110
+ if ( state.filterType === 'lowpass' ) {
111
+ // H(s) = 1 / (s² + s/Q + 1) -> H(z) via bilinear transform
112
+ state.b0 = K2 * norm;
113
+ state.b1 = 2 * K2 * norm;
114
+ state.b2 = K2 * norm;
115
+ state.a1 = 2 * ( K2 - 1 ) * norm;
116
+ state.a2 = ( K2 - ( K / Q ) + 1 ) * norm;
117
+ } else { // highpass
118
+ // H(s) = s² / (s² + s/Q + 1) -> H(z) via bilinear transform
119
+ state.b0 = norm;
120
+ state.b1 = -2 * norm;
121
+ state.b2 = norm;
122
+ state.a1 = 2 * ( K2 - 1 ) * norm;
123
+ state.a2 = ( K2 - ( K / Q ) + 1 ) * norm;
124
+ }
125
+
126
+ // Validate filter stability (poles inside unit circle)
127
+ /* c8 ignore next 5 -- defensive: bilinear transform produces stable coefficients for valid Nyquist-bounded cutoff */
128
+ if ( Math.abs( state.a2 ) >= 1 ) {
129
+ throw new Error(
130
+ `Filter unstable: coefficient a2 (${state.a2.toFixed(4)}) is outside stability bounds`
131
+ );
132
+ }
133
+
134
+ /* c8 ignore next 5 -- defensive: same Nyquist guard prevents a1 stability violation */
135
+ if ( Math.abs( state.a1 ) >= 1 + state.a2 ) {
136
+ throw new Error(
137
+ `Filter unstable: coefficient a1 (${state.a1.toFixed(4)}) violates stability condition |a1| < 1 + a2`
138
+ );
139
+ }
140
+
141
+ // State variables for Direct Form II
142
+ state.z1 = 0;
143
+ state.z2 = 0;
144
+
145
+ // CRITICAL: Initialize output storage for publishTo()
146
+ // This ensures edge cases (NaN/Inf) work correctly on first call
147
+ state.output = 0;
148
+
149
+ // Initialization strategy
150
+ if ( spec.initStrategy === 'dc' && spec.dcEstimate !== undefined ) {
151
+ // Initialize DF2T state to steady-state for known DC level.
152
+ // At steady state with constant input D: output = D·G, z1 = G·D - b0·D,
153
+ // z2 = b2·D - a2·G·D, where G = H(1) = Σb / (1 + a1 + a2).
154
+ const dcGain = ( state.b0 + state.b1 + state.b2 ) / ( 1 + state.a1 + state.a2 );
155
+ if ( Math.abs( dcGain ) > 1e-10 ) {
156
+ const steadyOutput = spec.dcEstimate * dcGain;
157
+ state.z1 = steadyOutput - ( state.b0 * spec.dcEstimate );
158
+ state.z2 = ( state.b2 * spec.dcEstimate ) - ( state.a2 * steadyOutput );
159
+ state.output = steadyOutput;
160
+ }
161
+ }
162
+
163
+ // Auto-compute performance characteristics
164
+ state.performance = {
165
+ multipliesPerSample: 5,
166
+ addsPerSample: 4,
167
+ memoryBytes: 56, // 7 Float64s (6 coeffs + 2 state + 1 output)
168
+ // Group delay approximation at cutoff (in samples)
169
+ groupDelaySamples: Math.round( 0.5 / ( Math.PI * normalizedCutoff ) ),
170
+ // Settling time to 98% (in samples)
171
+ settlingTimeSamples: Math.round( 4 / ( 2 * Math.PI * normalizedCutoff ) ),
172
+ // Useful for debugging
173
+ actualCutoffHz: cutoffHz,
174
+ normalizedCutoff: normalizedCutoff
175
+ };
176
+
177
+ // Output configuration
178
+ state.stats = spec.stats;
179
+ state.nodeType = introspect.getNodeType();
180
+
181
+ return state;
182
+ }; // init()
183
+
184
+ export default init;
@@ -0,0 +1,228 @@
1
+ /**
2
+ * @fileoverview Introspection metadata for butterworth-filter node.
3
+ *
4
+ * Defines supported stats, control methods, capabilities, DSL schema,
5
+ * and default options. All getters return defensive copies.
6
+ */
7
+
8
+ import { validators } from '../../core/utils/validate/index.js';
9
+
10
+ /** Single source of truth for capabilities */
11
+ const SUPPORTED_STATS = [ 'filtered' ];
12
+
13
+ const STAT_DESCRIPTIONS = {
14
+ filtered: 'Butterworth filtered signal'
15
+ };
16
+
17
+ /** Control methods available for triggers */
18
+ const SUPPORTED_CONTROL_METHODS = {
19
+ reset: 'Clears accumulated state and restarts computation',
20
+ enable: 'Enables node processing',
21
+ disable: 'Disables node processing',
22
+ pause: 'Pauses node processing while keeping state visible',
23
+ unpause: 'Resumes node processing after pause'
24
+ };
25
+
26
+ const NODE_TYPE = 'Butterworth Filter';
27
+
28
+ /** Node capabilities for documentation */
29
+ const CAPABILITIES = {
30
+ description: 'High-performance 2nd-order Butterworth filter for real-time streaming',
31
+ features: [
32
+ 'Lowpass and highpass filtering',
33
+ 'Multiple cutoff specification methods',
34
+ 'DC initialization for transient reduction',
35
+ 'Cascade adjustment for multi-stage filtering'
36
+ ]
37
+ };
38
+
39
+ // Static metadata exports
40
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
41
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
42
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
43
+ export const getNodeType = () => NODE_TYPE;
44
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
45
+
46
+ export const DEFAULT_OPTIONS = {
47
+ filterType: 'lowpass',
48
+ acceptNumericalRisk: false
49
+ };
50
+
51
+ // DSL Metadata for transpilation
52
+ const DSL_METADATA = {
53
+ // Spec schema for validation
54
+ specSchema: {
55
+ nodeType: {
56
+ type: 'string',
57
+ required: true,
58
+ value: NODE_TYPE
59
+ },
60
+ name: {
61
+ type: 'string',
62
+ required: true,
63
+ validator: validators.identifier,
64
+ error: 'Name must be a valid identifier'
65
+ },
66
+ from: {
67
+ type: 'object',
68
+ required: true,
69
+ properties: {
70
+ x: {
71
+ type: 'string',
72
+ required: true,
73
+ validator: validators.noSpaces,
74
+ error: 'Field name cannot contain spaces'
75
+ }
76
+ }
77
+ },
78
+ filterType: {
79
+ type: 'stringOrFieldKeyed',
80
+ required: false,
81
+ default: 'lowpass',
82
+ validator: validators.oneOf([ 'lowpass', 'highpass' ]),
83
+ error: 'filterType must be "lowpass" or "highpass"'
84
+ },
85
+ sampleRateHz: {
86
+ type: 'number',
87
+ required: true,
88
+ validator: validators.positive,
89
+ error: 'sampleRateHz must be positive'
90
+ },
91
+ // One of these cutoff specifications is required
92
+ cutoffHz: {
93
+ type: 'numberOrFieldKeyed',
94
+ required: false,
95
+ validator: validators.positive,
96
+ error: 'cutoffHz must be positive'
97
+ },
98
+ settlingTimeMs: {
99
+ type: 'numberOrFieldKeyed',
100
+ required: false,
101
+ validator: validators.positive,
102
+ error: 'settlingTimeMs must be positive'
103
+ },
104
+ cutoffRatio: {
105
+ type: 'numberOrFieldKeyed',
106
+ required: false,
107
+ validator: validators.inRange(0, 1),
108
+ error: 'cutoffRatio must be between 0 and 1'
109
+ },
110
+ adjustForCascade: {
111
+ type: 'number',
112
+ required: false,
113
+ integer: true,
114
+ min: 2,
115
+ validator: validators.positiveInteger,
116
+ error: 'adjustForCascade must be an integer >= 2'
117
+ },
118
+ initStrategy: {
119
+ type: 'string',
120
+ required: false,
121
+ validator: validators.oneOf([ 'dc' ]),
122
+ error: 'initStrategy must be "dc" if specified'
123
+ },
124
+ dcEstimate: {
125
+ type: 'number',
126
+ required: false
127
+ },
128
+ acceptNumericalRisk: {
129
+ type: 'boolean',
130
+ required: false,
131
+ default: false
132
+ },
133
+ stats: {
134
+ type: 'object',
135
+ required: true,
136
+ propertyNames: SUPPORTED_STATS,
137
+ propertySchema: {
138
+ type: 'object',
139
+ required: true,
140
+ properties: {
141
+ storeAs: {
142
+ type: 'string',
143
+ required: true,
144
+ validator: validators.identifier,
145
+ error: 'storeAs must be a valid identifier'
146
+ }
147
+ }
148
+ }
149
+ }
150
+ },
151
+
152
+ // Cross-field validators
153
+ crossFieldValidators: [
154
+ {
155
+ fields: [ 'cutoffHz', 'settlingTimeMs', 'cutoffRatio' ],
156
+ // At least one cutoff specification method must be provided
157
+ validator: ( spec ) => ( spec.cutoffHz || spec.settlingTimeMs || spec.cutoffRatio ),
158
+ error: 'Must specify cutoffHz, settlingTimeMs, or cutoffRatio'
159
+ },
160
+ {
161
+ fields: [ 'cutoffHz', 'settlingTimeMs', 'cutoffRatio' ],
162
+ validator: ( spec ) => {
163
+ // Only one cutoff specification method should be provided
164
+ const methods = [ spec.cutoffHz, spec.settlingTimeMs, spec.cutoffRatio ].filter( Boolean );
165
+ return methods.length === 1;
166
+ },
167
+ error: 'Specify only one of: cutoffHz, settlingTimeMs, or cutoffRatio'
168
+ },
169
+ {
170
+ fields: [ 'initStrategy', 'dcEstimate' ],
171
+ validator: ( spec ) => {
172
+ // If initStrategy is 'dc', dcEstimate must be provided
173
+ if ( spec.initStrategy === 'dc' ) {
174
+ return spec.dcEstimate !== undefined;
175
+ }
176
+ return true;
177
+ },
178
+ error: 'dcEstimate is required when initStrategy is "dc"'
179
+ },
180
+ {
181
+ fields: [ 'initStrategy', 'dcEstimate' ],
182
+ validator: ( spec ) => {
183
+ // If dcEstimate is provided, initStrategy must be 'dc'
184
+ if ( spec.dcEstimate !== undefined ) {
185
+ return spec.initStrategy === 'dc';
186
+ }
187
+ return true;
188
+ },
189
+ error: 'initStrategy must be "dc" when dcEstimate is provided'
190
+ }
191
+ ],
192
+
193
+ // How to build the spec from DSL params
194
+ // Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
195
+ buildSpec: ( name, x, stats, options ) => ( {
196
+ nodeType: NODE_TYPE,
197
+ name,
198
+ from: { x },
199
+ stats,
200
+ ...options
201
+ } ),
202
+
203
+ };
204
+
205
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
206
+
207
+ // Performance and design info exports
208
+ export const getPerformance = ( state ) => ( { ...state.performance } );
209
+
210
+ export const getDesignInfo = ( state ) => ( {
211
+ filterType: state.filterType,
212
+ actualCutoffHz: state.config.cutoffHz,
213
+ sampleRateHz: state.config.sampleRateHz,
214
+ configMethod: state.config.intent,
215
+ normalized3dBFreq: state.config.normalizedCutoff,
216
+ cascadeAdjustment: state.config.cascadeAdjustment,
217
+ // Phase delay at cutoff in ms
218
+ phaseDelayMs: ( state.performance.groupDelaySamples / state.config.sampleRateHz ) * 1000,
219
+ // Settling time in ms
220
+ settlingTimeMs: ( state.performance.settlingTimeSamples / state.config.sampleRateHz ) * 1000
221
+ } );
222
+
223
+ // Human-readable description
224
+ export const describe = ( state ) => {
225
+ const info = getDesignInfo( state );
226
+ return `${state.filterType} filter: ${info.actualCutoffHz.toFixed(1)}Hz @ ${info.sampleRateHz}Hz ` +
227
+ `(settles in ~${info.settlingTimeMs.toFixed(0)}ms, delay ~${info.phaseDelayMs.toFixed(1)}ms)`;
228
+ };
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Publish function for butterworth-filter node.
3
+ *
4
+ * Copies the filtered output to the message, or propagates NaN if the last
5
+ * input was invalid. Skipped entirely when disabled.
6
+ */
7
+
8
+ import { publishNaN } from '../../core/utils/node/index.js';
9
+
10
+ const publishTo = function ( state, msg ) {
11
+ if ( state.disable ) return;
12
+ // Propagate NaN if unhealthy
13
+ if ( state.inputValidationFailed ) {
14
+ publishNaN( state, msg );
15
+ return;
16
+ }
17
+
18
+ msg[ state.stats.filtered.storeAs ] = state.output;
19
+ }; // publishTo()
20
+
21
+ export default publishTo;
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview Recompute for butterworth-filter node.
3
+ *
4
+ * No-op: stable 2nd-order IIR — state variables z1/z2 are bounded by
5
+ * exponential decay. No accumulated statistics susceptible to drift.
6
+ */
7
+
8
+ const recompute = function ( _state ) {
9
+ // Stable 2nd-order IIR: z1/z2 bounded by exponential decay; no drift.
10
+ return true;
11
+ }; // recompute()
12
+
13
+ export default recompute;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @fileoverview Reset for butterworth-filter node.
3
+ *
4
+ * Clears delay elements and output. When a DC estimate is configured,
5
+ * restores steady-state z1/z2 and output to minimize transient on restart.
6
+ */
7
+
8
+ const reset = function ( state ) {
9
+ // Clear delay elements
10
+ state.z1 = 0;
11
+ state.z2 = 0;
12
+
13
+ // Clear output
14
+ state.output = 0;
15
+
16
+ // Re-initialize to steady-state if DC estimate is known
17
+ if ( state.config.dcEstimate !== undefined ) {
18
+ const dcGain = ( state.b0 + state.b1 + state.b2 ) / ( 1 + state.a1 + state.a2 );
19
+ if ( Math.abs( dcGain ) > 1e-10 ) {
20
+ const steadyOutput = state.config.dcEstimate * dcGain;
21
+ state.z1 = steadyOutput - ( state.b0 * state.config.dcEstimate );
22
+ state.z2 = ( state.b2 * state.config.dcEstimate ) - ( state.a2 * steadyOutput );
23
+ state.output = steadyOutput;
24
+ }
25
+ }
26
+
27
+ return true;
28
+ }; // reset()
29
+
30
+ export default reset;
@@ -0,0 +1,38 @@
1
+ /**
2
+ * @fileoverview Update function for butterworth-filter node.
3
+ *
4
+ * Implements Direct Form II Transposed structure for a 2nd-order Butterworth
5
+ * filter. Denormals are flushed to zero to avoid performance degradation on
6
+ * x86 hardware.
7
+ */
8
+
9
+ const DENORMAL_THRESHOLD = 1e-30;
10
+
11
+ const update = function ( state, msg ) {
12
+ if ( state.disable || state.pause ) return state;
13
+ const xVal = msg[ state.x ];
14
+ // Reset on each update
15
+ state.inputValidationFailed = false;
16
+ // Handle faults gracefully: ensure their isolation
17
+ if ( !Number.isFinite( xVal ) ) {
18
+ // Signals publishing NaN for all demanded `stats` in publishTo.
19
+ state.inputValidationFailed = true;
20
+ return state;
21
+ }
22
+
23
+ // Direct Form II implementation
24
+ const output = ( state.b0 * xVal ) + state.z1;
25
+ state.z1 = ( state.b1 * xVal ) - ( state.a1 * output ) + state.z2;
26
+ state.z2 = ( state.b2 * xVal ) - ( state.a2 * output );
27
+
28
+ // Flush denormals to zero (performance optimization)
29
+ if ( Math.abs( state.z1 ) < DENORMAL_THRESHOLD ) state.z1 = 0;
30
+ if ( Math.abs( state.z2 ) < DENORMAL_THRESHOLD ) state.z2 = 0;
31
+
32
+ // Store output in state for publishTo() to access
33
+ state.output = output;
34
+
35
+ return state;
36
+ }; // update()
37
+
38
+ export default update;
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @fileoverview Entry point for categorize node.
3
+ *
4
+ * Re-exports all lifecycle functions (init, update, publishTo, reset, recompute),
5
+ * shared control signals (disable, enable, pause, unpause), and introspection metadata.
6
+ */
7
+
8
+ export { default as init } from './init.js';
9
+ export { default as update } from './update.js';
10
+ export { default as reset } from './reset.js';
11
+ export { default as recompute } from './recompute.js';
12
+ export { default as publishTo } from './publish-to.js';
13
+ // Direct imports for common control functions
14
+ export { default as disable } from '../../core/utils/node/disable.js';
15
+ export { default as enable } from '../../core/utils/node/enable.js';
16
+ export { default as pause } from '../../core/utils/node/pause.js';
17
+ export { default as unpause } from '../../core/utils/node/unpause.js';
18
+ // Re-export everything from introspection.js
19
+ export * from './introspect.js';
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @fileoverview Initialization for categorize node.
3
+ *
4
+ * Validates the spec, resolves thresholds (static or tunable via asTunable),
5
+ * resolves field-keyed categories, and builds the initial state with the
6
+ * default category (first element).
7
+ */
8
+
9
+ import { validateSpec } from '../../core/utils/node/index.js';
10
+ import * as introspect from './introspect.js';
11
+ import { resolveArray } from '../../core/utils/options/resolve-field-keyed.js';
12
+ import { asTunable } from '../../core/tunable/index.js';
13
+
14
+ const init = function ( spec ) {
15
+ validateSpec( spec, introspect );
16
+
17
+ // Create state after validation passes
18
+ const state = Object.create( null );
19
+ // Standard input parameter health flag during update: false means healthy
20
+ state.inputValidationFailed = false;
21
+ // Ensure node is enabled by default
22
+ state.disable = false;
23
+ state.pause = false;
24
+
25
+ // Extract configuration
26
+ state.x = spec.from.x;
27
+ state.stats = spec.stats;
28
+
29
+ // Supports direct: { thresholds: [15, 25] }
30
+ // field-keyed: { thresholds: { temp: [15, 25], pressure: [30, 60] } }
31
+ // and tunable: { thresholds: lookupByField('shift', { day: [...], night: [...] }, [...]) }
32
+ if ( typeof spec.thresholds === 'function' ) {
33
+ // Dynamic thresholds (tunable) - resolve at runtime
34
+ state.thresholdsFn = spec.thresholds;
35
+ state.resolvedThresholds = null; // Not yet resolved — first message must succeed
36
+ } else {
37
+ // Static thresholds - resolve field-keying at init time
38
+ const thresholdsResolved = resolveArray( spec.thresholds, state.x );
39
+ state.thresholdsFn = asTunable( thresholdsResolved );
40
+ state.resolvedThresholds = thresholdsResolved; // Known at init
41
+ }
42
+ state.tunableErrorLogged = false;
43
+
44
+ // Categories remain static (structural)
45
+ state.categories = resolveArray( spec.categories, state.x );
46
+
47
+ // Current categorization state
48
+ state.categoryIndex = 0;
49
+ state.category = state.categories[ 0 ]; // Default to first category
50
+
51
+ // Node metadata
52
+ state.nodeType = introspect.getNodeType();
53
+ state.inControlPhase = false;
54
+
55
+ return state;
56
+ }; // init()
57
+
58
+ export default init;