@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,132 @@
1
+ // nodes/moments-digest/index.js
2
+
3
+ /**
4
+ * @fileoverview
5
+ * Moments Digest — numerically stable, windowed M1..M4 with min/max, designed for
6
+ * arbitrary-depth cascades (fan-in = 1 per chain) using a single shared field space.
7
+ *
8
+ * ────────────────────────────────────────────────────────────────────────────────
9
+ * TL;DR
10
+ * • Consumes raw samples `msg[ x ]` and produces per-window statistics:
11
+ * `x_n, x_M1, x_M2, x_M3, x_M4, x_min, x_max` plus a boolean `msg[ name ] = true`.
12
+ * • Central moments are carried as unnormalized sums (Pébay 2008):
13
+ * M2 = Σ (x−μ)², M3 = Σ (x−μ)³, M4 = Σ (x−μ)⁴. Compute variance/kurtosis downstream.
14
+ * • Supports arbitrary cascade depth: root and all downstream levels read/write the SAME `x_*` keys.
15
+ * • Publishes only on **window completion** or **flush**; on all other ticks it **scrubs** `x_*`
16
+ * to `undefined` so downstream nodes cannot accidentally ingest partials.
17
+ * • Uniform flush key: `x_flush`. **Only the root sets it;** cascades react to it.
18
+ *
19
+ * ────────────────────────────────────────────────────────────────────────────────
20
+ * Execution model (per message)
21
+ * The node is split into three conceptual phases. All state mutation happens in
22
+ * `update()`; `publishTo()` is a pure renderer of what `update()` planned.
23
+ *
24
+ * 1) Prelude (top of update)
25
+ * • Clear last tick's publish plan: `planPublish = false`, `propagateFlush = false`.
26
+ * • ROOT ONLY: If flush is latched:
27
+ * – If `n > 0`: **copy → reset → planPublish** (publish the snapshot this tick).
28
+ * Root sets `propagateFlush = true` so `publishTo()` will emit `x_flush`.
29
+ * – If `n === 0`: **signal-only flush**. Root sets `propagateFlush = true`,
30
+ * but does not plan a stats publish (no data to emit).
31
+ * • CASCADE: Does NOT handle flush in prelude (deferred to after ingestion).
32
+ *
33
+ * 2) Core update
34
+ * • Raw level: read `msg[ x ]`; if non-finite → mark input invalid and return (no plan).
35
+ * Otherwise, update moments and counters with the new sample.
36
+ * • Cascade level:
37
+ * – **Always merge when** `msg[ x_n ] !== undefined` (ingest parent's moments).
38
+ * – **After ingestion**, if `msg[ x_flush ] === true` and `n > 0`:
39
+ * **copy → reset → planPublish** (ensures cascade includes parent's flush data).
40
+ *
41
+ * 3) Epilogue (end of update)
42
+ * • If the window just completed (and no flush snapshot was already planned, and input is valid):
43
+ * – **copy → reset → planPublish** (publish the completed window this tick).
44
+ *
45
+ * 4) publishTo (pure render; no state mutation)
46
+ * • If `propagateFlush === true` and this is the root: set `msg[ x_flush ] = true`.
47
+ * • If `planPublish === true`: write all `x_*` fields **from the snapshot** and set `msg[ name ] = true`.
48
+ * • Otherwise (non-publish tick): scrub `x_*` to `undefined`. Never touch `x_flush`.
49
+ *
50
+ * ────────────────────────────────────────────────────────────────────────────────
51
+ * "Before/After" semantics (UX via placement)
52
+ * • Controller **upstream of the root** ("BEFORE"):
53
+ * – Flush latch is seen in Prelude on the **same** message → snapshot/reset now.
54
+ * The current message becomes the **first sample of the next window**.
55
+ * Root publishes the snapshot and emits `x_flush`; cascades see `x_flush`,
56
+ * ingest parent's moments FIRST, then publish their accumulated state and reset.
57
+ * • Controller **downstream of the root** ("AFTER"):
58
+ * – Flush latch is seen on the **next** message. The previous message is included
59
+ * in the snapshot that publishes next tick. (If no next message arrives, nothing
60
+ * triggers the deferred publish — controller may emit a final synthetic tick if needed.)
61
+ *
62
+ * ────────────────────────────────────────────────────────────────────────────────
63
+ * Inputs
64
+ * • from.x : string (required) → base field name, e.g., 'temperature'.
65
+ * • windowSize : number (required) → samples per window at this level (≥ 4).
66
+ * • name : string (optional) → node identifier; sets `msg[ name ] = true` on publish.
67
+ * • cascade : boolean (optional) → false for raw level, true for cascade level.
68
+ *
69
+ * ────────────────────────────────────────────────────────────────────────────────
70
+ * Outputs (on publish)
71
+ * • msg[ x + '_n' ] : number
72
+ * • msg[ x + '_M1' ] : number (mean)
73
+ * • msg[ x + '_M2' ] : number (Σ centered squares)
74
+ * • msg[ x + '_M3' ] : number (Σ centered cubes)
75
+ * • msg[ x + '_M4' ] : number (Σ centered fourth powers)
76
+ * • msg[ x + '_min' ] : number
77
+ * • msg[ x + '_max' ] : number
78
+ * • msg[ name ] : true (flag marking "this node published now")
79
+ * • msg[ x + '_flush' ] : true (root only, on flush ticks)
80
+ *
81
+ * On non-publish cycles, **all** `x_*` fields are set to `undefined` (keys kept, never deleted).
82
+ *
83
+ * ────────────────────────────────────────────────────────────────────────────────
84
+ * Flush semantics (action + state)
85
+ * • Root:
86
+ * – A flush request latches a boundary. In Prelude the node **copies → resets → plans**
87
+ * a publish from the snapshot (if `n > 0`) and will **emit `x_flush`** at publish time.
88
+ * – If `n === 0`, it is a **signal-only** flush: emit `x_flush` without stats.
89
+ * • Cascades:
90
+ * – Never set/clear `x_flush`. They **observe** `x_flush` and publish accumulated state.
91
+ * – Critical: cascades ingest parent's moments BEFORE handling flush, ensuring
92
+ * parent's flush data is included in cascade's flush output (no data loss).
93
+ *
94
+ * ────────────────────────────────────────────────────────────────────────────────
95
+ * NaN / invalid input handling
96
+ * • Raw level:
97
+ * – A non-finite current input suppresses planning of a **window** publish.
98
+ * – A **flush snapshot planned in Prelude** still publishes this tick; `publishTo()`
99
+ * does not early-bail on invalid input. This preserves the boundary and keeps
100
+ * cascades aligned (root still emits `x_flush`).
101
+ * • Cascade level:
102
+ * – No raw input is read. The node merges only when `x_*` fields are present.
103
+ *
104
+ * ────────────────────────────────────────────────────────────────────────────────
105
+ * Invariants
106
+ * • Uniform flush key across the chain: `x_flush` (root only writes).
107
+ * • No data loss: cascades always ingest before responding to flush.
108
+ * • Scrub on every non-publish tick: `x_*` → `undefined`.
109
+ * • Publish is **snapshot-only**; `publishTo()` never mutates node state.
110
+ * • O(1) time and O(1) memory per message; no hot-path allocations (snapshot object is reused).
111
+ *
112
+ * ────────────────────────────────────────────────────────────────────────────────
113
+ * References
114
+ * [1] Pébay, P. (2008). Formulas for robust, one-pass parallel computation of covariances
115
+ * and arbitrary-order statistical moments. Sandia Report SAND2008-6212.
116
+ * [2] Chan, T. F., Golub, G. H., & LeVeque, R. J. (1983). Algorithms for computing the
117
+ * sample variance: Analysis and recommendation. The American Statistician, 37(3), 242-247.
118
+ */
119
+
120
+ export { default as init } from './init.js';
121
+ export { default as update } from './update.js';
122
+ export { default as reset } from './reset.js';
123
+ export { default as recompute } from './recompute.js';
124
+ export { default as publishTo } from './publish-to.js';
125
+ export { default as flush } from './flush.js';
126
+ // Direct imports for common control functions
127
+ export { default as disable } from '../../core/utils/node/disable.js';
128
+ export { default as enable } from '../../core/utils/node/enable.js';
129
+ export { default as pause } from '../../core/utils/node/pause.js';
130
+ export { default as unpause } from '../../core/utils/node/unpause.js';
131
+ // Re-export everything from introspection.js
132
+ export * from './introspect.js';
@@ -0,0 +1,84 @@
1
+ // nodes/moments-digest/init.js
2
+
3
+ import { validateSpec } from '../../core/utils/node/index.js';
4
+ import * as introspect from './introspect.js';
5
+ import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
6
+
7
+ const init = function ( spec ) {
8
+ validateSpec( spec, introspect );
9
+
10
+ // Create state after validation passes
11
+ const state = Object.create( null );
12
+ // Standard input parameter health flag during update: false means healthy
13
+ state.inputValidationFailed = false;
14
+ // Ensure node is enabled by default
15
+ state.disable = false;
16
+ state.pause = false;
17
+ // Preserve node's name
18
+ state.name = spec.name;
19
+
20
+ // Extract configuration
21
+ state.x = spec.from.x;
22
+
23
+ // Apply defaults from introspect (validation doesn't enforce them)
24
+ // Supports both direct and field-keyed specification
25
+ const windowSizeSpec = resolveScalar( spec.windowSize, state.x );
26
+ state.windowSize = windowSizeSpec ?? introspect.DEFAULT_OPTIONS.windowSize;
27
+
28
+ // Determine mode: raw or cascade
29
+ state.isCascading = spec.cascade || introspect.DEFAULT_OPTIONS.cascade;
30
+
31
+ // Extract base field name
32
+ state.baseField = state.x;
33
+
34
+ // Pre-compute ALL field names for hot path (no string concat in update!)
35
+ // Input names are same output names in the case of cascade
36
+ state.fieldMap = Object.create( null );
37
+ state.fieldMap.n = state.baseField + '_n';
38
+ state.fieldMap.M1 = state.baseField + '_M1';
39
+ state.fieldMap.M2 = state.baseField + '_M2';
40
+ state.fieldMap.M3 = state.baseField + '_M3';
41
+ state.fieldMap.M4 = state.baseField + '_M4';
42
+ state.fieldMap.min = state.baseField + '_min';
43
+ state.fieldMap.max = state.baseField + '_max';
44
+
45
+
46
+ // Pébay algorithm state
47
+ state.n = 0;
48
+ state.M1 = 0;
49
+ state.M2 = 0;
50
+ state.M3 = 0;
51
+ state.M4 = 0;
52
+ state.min = Infinity;
53
+ state.max = -Infinity;
54
+ // Signal for digest completion.
55
+ state[ state.name ] = false;
56
+ // This is used to signal downstream nodes to flush as the primary control
57
+ // path works only for the root (i.e. the first or the non-cascade one) node
58
+ // in the chain. Control signalling for cascaded nodes is disabled as the
59
+ // chain sequence for flush operation needs to be respected by the downstream
60
+ // nodes. This flag is set to true by the root node on flush event and is used
61
+ // subsequently by the cascaded downstream nodes to flush accordingly. BUT
62
+ // we need to create the **key** irrespective of being root or cascade.
63
+ state.flushSignalKey = state.x + '_flush'; // e.g. 'temperature_flush'
64
+
65
+
66
+ // Window management
67
+ state.currentCount = 0;
68
+ state.windowComplete = false;
69
+
70
+ // Set by flush method
71
+ state.flushLatched = false;
72
+ // Publish stats (from snapshot) this tick
73
+ state.planPublish = false;
74
+ // Root should emit x_flush this tick
75
+ state.propagateFlush = false;
76
+ // Always copy moments here on window completion or flush event
77
+ state.snapshot = Object.create( null );
78
+
79
+ state.nodeType = introspect.getNodeType();
80
+
81
+ return state;
82
+ }; // init()
83
+
84
+ export default init;
@@ -0,0 +1,122 @@
1
+ // nodes/moments-digest/introspect.js
2
+
3
+ import { validators } from '../../core/utils/validate/index.js';
4
+
5
+ /** Single source of truth for what this plugin can do */
6
+ const SUPPORTED_STATS = [
7
+ 'n', // Sample count
8
+ 'M1', // Mean (first moment)
9
+ 'M2', // Second central moment
10
+ 'M3', // Third central moment
11
+ 'M4', // Fourth central moment
12
+ 'min', // Minimum value
13
+ 'max' // Maximum value
14
+ ];
15
+
16
+ /** Human-readable descriptions (for introspection) */
17
+ const STAT_DESCRIPTIONS = {
18
+ n: 'Number of samples in the aggregated window',
19
+ M1: 'Arithmetic mean of values in the window',
20
+ M2: 'Second central moment (sum of squared deviations from mean)',
21
+ M3: 'Third central moment (sum of cubed deviations from mean)',
22
+ M4: 'Fourth central moment (sum of quartic deviations from mean)',
23
+ min: 'Minimum value across all samples',
24
+ max: 'Maximum value across all samples'
25
+ };
26
+
27
+ /** Control methods available for triggers */
28
+ const SUPPORTED_CONTROL_METHODS = {
29
+ reset: 'Clears accumulated state and restarts computation',
30
+ enable: 'Enables node processing',
31
+ disable: 'Disables node processing',
32
+ pause: 'Pauses node processing while keeping state visible',
33
+ unpause: 'Resumes node processing after pause',
34
+ flush: 'Forces immediate output of partial window'
35
+ };
36
+
37
+ /** Node capabilities for documentation */
38
+ const CAPABILITIES = {
39
+ description: 'High-performance streaming aggregation using Pébay\'s numerically stable algorithm for statistical moments',
40
+ features: [
41
+ 'Configurable window-based aggregation (4-1024 samples)',
42
+ 'Numerically stable incremental mean and moment computation',
43
+ 'Cascade mode for multi-level aggregation (seconds → minutes → hours)',
44
+ 'Automatic mean and moment combining for cascaded windows',
45
+ 'Window completion signaling via msg[nodeName] = true',
46
+ 'Intelligent message filtering in cascade mode (processes only moment messages)',
47
+ 'NaN resilience - skips invalid samples without corrupting statistics',
48
+ 'Zero allocations in hot path for maximum throughput',
49
+ 'Flush support for graceful shutdown with incomplete windows'
50
+ ]
51
+ };
52
+
53
+ /** The type of this node */
54
+ const NODE_TYPE = 'Moments Digest';
55
+
56
+ // Attach static metadata once, from our constants by making a shallow copy.
57
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
58
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
59
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
60
+ export const getNodeType = () => NODE_TYPE;
61
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
62
+
63
+ export const DEFAULT_OPTIONS = {
64
+ windowSize: 100,
65
+ cascade: false
66
+ };
67
+
68
+ // DSL Metadata for transpilation
69
+ const DSL_METADATA = {
70
+ // Spec schema for validation
71
+ specSchema: {
72
+ nodeType: {
73
+ type: 'string',
74
+ required: true,
75
+ value: NODE_TYPE
76
+ },
77
+ name: {
78
+ type: 'string',
79
+ required: true,
80
+ validator: validators.identifier,
81
+ error: 'Name must be a valid identifier'
82
+ },
83
+ from: {
84
+ type: 'object',
85
+ required: true,
86
+ properties: {
87
+ x: {
88
+ type: 'string',
89
+ required: true,
90
+ validator: validators.noSpaces,
91
+ error: 'Field name cannot contain spaces'
92
+ }
93
+ }
94
+ },
95
+ windowSize: {
96
+ type: 'numberOrFieldKeyed',
97
+ required: false,
98
+ default: DEFAULT_OPTIONS.windowSize,
99
+ integer: true,
100
+ min: 4,
101
+ max: 1024,
102
+ validator: validators.positiveInteger,
103
+ error: 'Window size must be a positive integer'
104
+ },
105
+ cascade: {
106
+ type: 'boolean',
107
+ required: false,
108
+ default: DEFAULT_OPTIONS.cascade
109
+ }
110
+ },
111
+
112
+ // How to build the spec from DSL params
113
+ buildSpec: ( name, x, options ) => ( {
114
+ nodeType: NODE_TYPE,
115
+ name,
116
+ from: { x },
117
+ ...options
118
+ } ),
119
+
120
+ };
121
+
122
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,28 @@
1
+ // nodes/moments-digest/prelude.js
2
+
3
+ import { copyFrom } from './copy-from.js';
4
+ import reset from './reset.js';
5
+
6
+ export const prelude = function ( state, msg ) {
7
+ // Clear last tick’s plan
8
+ state.planPublish = false;
9
+ state.propagateFlush = false;
10
+
11
+ if ( !state.flushLatched && !msg[ state.flushSignalKey ] ) return;
12
+ // Either `state.flushLatched` or `msg[ state.flushSignalKey ]` is true
13
+ // implies that it is time to copy and plan a publish.
14
+
15
+ // Only ROOT nodes handle flush in prelude
16
+ if ( !state.isCascading ) {
17
+ state.propagateFlush = true;
18
+
19
+ if ( state.n > 0 ) {
20
+ copyFrom( state, state.snapshot ); // copy previous window
21
+ state.planPublish = true; // we will publish stats this tick
22
+ reset( state ); // exclude current message
23
+ }
24
+ }
25
+ // Otherwise: signal-only flush (no stats) via planPublish === false
26
+
27
+ state.flushLatched = false; // consume latch
28
+ }; // prelude()
@@ -0,0 +1,31 @@
1
+ // nodes/moments-digest/publish-to.js
2
+
3
+ const publishTo = function ( state, msg ) {
4
+ if ( state.disable ) return;
5
+ // Do not Skip publishing if we encountered NaN in this update cycle
6
+ // `update()` already suppresses planning on invalid samples;
7
+ // a flush snapshot planned in `prelude()` must still render.
8
+ const f = state.fieldMap;
9
+
10
+ if ( state.propagateFlush && ( !state.isCascading ) ) {
11
+ msg[ state.flushSignalKey ] = true;
12
+ }
13
+
14
+ if ( state.planPublish ) {
15
+ const s = state.snapshot;
16
+ msg[ f.n ] = s.n;
17
+ msg[ f.M1 ] = s.M1;
18
+ msg[ f.M2 ] = s.M2;
19
+ msg[ f.M3 ] = s.M3;
20
+ msg[ f.M4 ] = s.M4;
21
+ msg[ f.min ] = s.min;
22
+ msg[ f.max ] = s.max;
23
+ msg[ state.name ] = true;
24
+ return;
25
+ }
26
+
27
+ // signal-only or no publish → scrub
28
+ msg[ f.n ] = msg[ f.M1 ] = msg[ f.M2 ] = msg[ f.M3 ] = msg[ f.M4 ] = msg[ f.min ] = msg[ f.max ] = undefined;
29
+ }; // publishTo()
30
+
31
+ export default publishTo;
@@ -0,0 +1,9 @@
1
+ // nodes/moments-digest/recompute.js
2
+
3
+ const recompute = function () {
4
+ // Stats digest doesn't need recomputation
5
+ // Moments are computed incrementally using numerically stable algorithm
6
+ return true;
7
+ }; // recompute()
8
+
9
+ export default recompute;
@@ -0,0 +1,20 @@
1
+ // nodes/moments-digest/reset.js
2
+
3
+ const reset = function ( state ) {
4
+ // Reset all statistical state
5
+ state.n = 0;
6
+ state.M1 = 0;
7
+ state.M2 = 0;
8
+ state.M3 = 0;
9
+ state.M4 = 0;
10
+ state.min = Infinity;
11
+ state.max = -Infinity;
12
+
13
+ // Reset window management
14
+ state.currentCount = 0;
15
+ state.windowComplete = false;
16
+
17
+ return true;
18
+ }; // reset()
19
+
20
+ export default reset;
@@ -0,0 +1,182 @@
1
+ // nodes/moments-digest/update.js
2
+
3
+ /**
4
+ * @fileoverview
5
+ * Moments Digest Update — Incremental moment accumulation with flush synchronization
6
+ *
7
+ * ────────────────────────────────────────────────────────────────────────────────
8
+ * Core Processing Logic
9
+ * • Raw nodes: Process individual samples using Pébay's algorithm
10
+ * • Cascade nodes: Merge moment sets from parent digests
11
+ * • Window completion: Both node types publish on window completion
12
+ * • Flush override: Immediate publish of partial windows
13
+ *
14
+ * ────────────────────────────────────────────────────────────────────────────────
15
+ * Flush Synchronization (Critical for Data Integrity)
16
+ *
17
+ * ROOT NODES:
18
+ * • Handle flush in prelude (snapshot → reset → plan)
19
+ * • Controller placement determines current message inclusion:
20
+ * - BEFORE: Exclude current (it starts next window)
21
+ * - AFTER: Include current (it's in the snapshot)
22
+ * • Propagate x_flush signal to downstream cascades
23
+ *
24
+ * CASCADE NODES:
25
+ * • Never snapshot/reset in prelude (only root does)
26
+ * • Process parent's moments FIRST (if present)
27
+ * • THEN check for flush signal to publish accumulated state
28
+ * • This sequence ensures cascade includes exactly what root decided
29
+ *
30
+ * Key invariant: Cascades ingest whatever root published before responding
31
+ * to flush. This prevents data loss regardless of controller placement.
32
+ *
33
+ * ────────────────────────────────────────────────────────────────────────────────
34
+ * Execution Flow (per message)
35
+ * 1. Process input (raw sample or parent moments)
36
+ * 2. For cascades with flush signal: snapshot → reset after ingestion
37
+ * 3. Check window completion (both node types)
38
+ * 4. Plan publish if window complete or flush triggered
39
+ */
40
+
41
+ /* eslint-disable camelcase */
42
+ import { copyFrom } from './copy-from.js';
43
+ import { prelude } from './prelude.js';
44
+ import reset from './reset.js';
45
+
46
+ // Pébay's algorithm for single value update
47
+ const updateFromRaw = function ( state, value ) {
48
+ const n1 = state.n;
49
+ state.n += 1;
50
+
51
+ const delta = value - state.M1;
52
+ const delta_n = delta / state.n;
53
+ const delta_n2 = delta_n * delta_n;
54
+ const term1 = delta * delta_n * n1;
55
+
56
+ // Update moments
57
+ state.M1 += delta_n;
58
+ state.M4 += ( term1 * delta_n2 * ( ( state.n * state.n ) - ( 3 * state.n ) + 3 ) ) +
59
+ ( 6 * delta_n2 * state.M2 ) - ( 4 * delta_n * state.M3 );
60
+ state.M3 += ( term1 * delta_n * ( state.n - 2 ) ) - ( 3 * delta_n * state.M2 );
61
+ state.M2 += term1;
62
+
63
+ // Update min/max
64
+ if ( value < state.min ) state.min = value;
65
+ if ( value > state.max ) state.max = value;
66
+ }; // updateFromRaw()
67
+
68
+ // Combine moments from cascaded input
69
+ const updateFromCascade = function ( state, n, M1, M2, M3, M4, min, max ) {
70
+ // Initialize combined state if first cascade
71
+ if ( state.n === 0 ) {
72
+ state.n = n;
73
+ state.M1 = M1;
74
+ state.M2 = M2;
75
+ state.M3 = M3;
76
+ state.M4 = M4;
77
+ state.min = min;
78
+ state.max = max;
79
+ return;
80
+ }
81
+
82
+ // Combine using parallel moment algorithm
83
+ const a_n = state.n;
84
+ const a_M1 = state.M1;
85
+ const a_M2 = state.M2;
86
+ const a_M3 = state.M3;
87
+ const a_M4 = state.M4;
88
+
89
+ const combined_n = a_n + n;
90
+ const delta = M1 - a_M1;
91
+ const delta2 = delta * delta;
92
+ const delta3 = delta * delta2;
93
+ const delta4 = delta2 * delta2;
94
+
95
+ state.M1 = ( ( a_n * a_M1 ) + ( n * M1 ) ) / combined_n;
96
+
97
+ state.M2 = a_M2 + M2 +
98
+ ( delta2 * a_n * n / combined_n );
99
+
100
+ state.M3 = a_M3 + M3 +
101
+ ( ( delta3 * a_n * n * ( a_n - n ) ) / ( combined_n * combined_n ) ) +
102
+ ( 3 * delta * ( ( a_n * M2 ) - ( n * a_M2 ) ) / combined_n );
103
+
104
+ state.M4 = a_M4 + M4 +
105
+ ( delta4 * a_n * n * ( ( a_n * a_n ) - ( a_n * n ) + ( n * n ) ) / ( combined_n * combined_n * combined_n ) ) +
106
+ ( 6 * delta2 * ( ( a_n * a_n * M2 ) + ( n * n * a_M2 ) ) / ( combined_n * combined_n ) ) +
107
+ ( 4 * delta * ( ( a_n * M3 ) - ( n * a_M3 ) ) / combined_n );
108
+
109
+ state.n = combined_n;
110
+
111
+ // Update min/max
112
+ if ( min < state.min ) state.min = min;
113
+ if ( max > state.max ) state.max = max;
114
+ }; // updateFromCascade()
115
+
116
+ const update = function ( state, msg ) {
117
+ if ( state.disable || state.pause ) return state;
118
+
119
+ prelude( state, msg );
120
+
121
+ // Reset on each update
122
+ state.inputValidationFailed = false;
123
+
124
+ if ( state.isCascading ) {
125
+ // Extract ALL moments using pre-computed field names
126
+ const fmap = state.fieldMap;
127
+
128
+ // Always process parent's moments if available
129
+ if ( msg[ fmap.n ] !== undefined ) {
130
+ updateFromCascade(
131
+ state,
132
+ msg[ fmap.n ],
133
+ msg[ fmap.M1 ],
134
+ msg[ fmap.M2 ],
135
+ msg[ fmap.M3 ],
136
+ msg[ fmap.M4 ],
137
+ msg[ fmap.min ],
138
+ msg[ fmap.max ]
139
+ );
140
+ state.currentCount += 1;
141
+ }
142
+
143
+ // Handle flush signal (overrides window completion)
144
+ if ( msg[ state.flushSignalKey ] === true && state.n > 0 ) {
145
+ copyFrom( state, state.snapshot );
146
+ state.planPublish = true;
147
+ reset( state );
148
+ return state; // Skip window completion check
149
+ }
150
+
151
+ // Fall through to shared window completion logic
152
+ } else {
153
+ const xVal = msg[ state.x ];
154
+ // Handle faults gracefully: ensure their isolation: applies to RAW updates only
155
+ if ( !Number.isFinite( xVal ) ) {
156
+ // Signals publishing NaN for all demanded `stats` in publishTo.
157
+ state.inputValidationFailed = true;
158
+ return state;
159
+ }
160
+ // Update using Pébay's algorithm
161
+ updateFromRaw( state, xVal );
162
+ // Increment window counter
163
+ state.currentCount += 1;
164
+ }
165
+
166
+ // Epilogue: shared window completion logic
167
+ state.windowComplete = state.currentCount >= state.windowSize;
168
+ // End-of-window: schedule a publish (unless a flush snapshot is already planned)
169
+ if ( state.windowComplete && ( !state.planPublish ) && ( !state.inputValidationFailed ) ) {
170
+ // copy completed window
171
+ copyFrom( state, state.snapshot );
172
+ // Will publish snapshot this tick
173
+ state.planPublish = true;
174
+ // No flush propagation for plain window completion
175
+ // Start next window cleanly
176
+ reset( state );
177
+ }
178
+
179
+ return state;
180
+ }; // update()
181
+
182
+ export default update;
@@ -0,0 +1,67 @@
1
+ // nodes/node-names.js
2
+
3
+ /**
4
+ * @fileoverview Single source of truth for valid node names.
5
+ *
6
+ * Used for validation when nodes appear in DSL specs. Modules are
7
+ * loaded lazily at runtime via load-node-module.js.
8
+ *
9
+ * Names are camelCase, matching DSL method names. Directory paths
10
+ * are derived via toKebab() (e.g., 'esMean' -> 'es-mean').
11
+ */
12
+
13
+ /**
14
+ * Set of valid node names for O(1) lookup.
15
+ * @type {Set<string>}
16
+ */
17
+ export const NODE_NAMES = new Set( [
18
+ 'accumulate',
19
+ 'appraise',
20
+ 'butterworthFilter',
21
+ 'categorize',
22
+ 'controller',
23
+ 'diff',
24
+ 'digestMoments',
25
+ 'dwellTimeTracker',
26
+ 'emitIf',
27
+ 'esCorrelation',
28
+ 'esMean',
29
+ 'esPairwiseCorrelation',
30
+ 'esStats',
31
+ 'kalman1d',
32
+ 'invertFlag',
33
+ 'kernel',
34
+ 'lag',
35
+ 'median3',
36
+ 'pageHinkley',
37
+ 'passIf',
38
+ 'persistIf',
39
+ 'persistenceCheck',
40
+ 'processIndex',
41
+ 'ratio',
42
+ 'sanitize',
43
+ 'spikeGuard',
44
+ 'stateChangeDetector',
45
+ 'momentsDigest',
46
+ 'swStats',
47
+ 'swingWatch',
48
+ 'tally',
49
+ 'threshold',
50
+ 'transform',
51
+ 'trend',
52
+ 'twStats',
53
+ 'unbalance',
54
+ 'vectorDistance',
55
+ 'winnow'
56
+ ] );
57
+
58
+ /**
59
+ * Checks if a node name is valid.
60
+ *
61
+ * @param {string} name - camelCase node name
62
+ * @returns {boolean} true if valid
63
+ */
64
+ export const isValidNodeName = function ( name ) {
65
+ return NODE_NAMES.has( name );
66
+ };
67
+