@winkjs/composer 0.0.2 → 0.4.1

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 +106 -80
  3. package/package.json +56 -28
  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,64 @@
1
+ /**
2
+ * @fileoverview Exponentially weighted Welford update for mean, variance,
3
+ * stdev, and derived signal-quality metrics (SNR, CV, zScore).
4
+ *
5
+ * Uses the numerically stable two-pass delta form:
6
+ * delta = x - mean; mean += alpha * delta;
7
+ * delta2 = x - mean; m2 = decay * m2 + alpha * delta * delta2
8
+ *
9
+ * The "unbiased" variance is normalized as m2/weightSum. This intentionally
10
+ * diverges from the pandas ewm().var(bias=False) Bessel-type correction;
11
+ * both converge as n -> infinity (weightSum -> 1). See golden-truth-es-stats.py
12
+ * for cross-validation details.
13
+ *
14
+ * Zero allocations; all reads/writes via the pre-allocated state object.
15
+ */
16
+ // nodes/es-stats/update-welford.js
17
+
18
+ const updateWelford = function ( state, xVal ) {
19
+ state.weightSum = ( state.decay * state.weightSum ) + state.alpha;
20
+ if ( state.weightSum > 1 ) state.weightSum = 1; // defensive clamp against drift
21
+
22
+ // Z-Score: How many standard deviations away from established mean?
23
+ // Computed BEFORE update to detect anomalies in incoming value
24
+ // Uses pre-update stats to preserve causality in anomaly detection
25
+ if ( state.stats.zScore ) {
26
+ if ( state.stdev > state.EPS ) {
27
+ // Use current baseline
28
+ state.zScore = ( xVal - state.mean ) / state.stdev;
29
+ } else {
30
+ state.zScore = 0;
31
+ }
32
+ }
33
+
34
+ // NOW incorporate this value into running statistics
35
+ const delta = xVal - state.mean;
36
+ state.mean += ( state.alpha * delta );
37
+ const delta2 = xVal - state.mean;
38
+
39
+ state.m2 = ( state.decay * state.m2 ) + ( state.alpha * delta * delta2 );
40
+
41
+ state.variance = state.biased ? state.m2 :
42
+ ( state.weightSum > state.EPS ? ( state.m2 / state.weightSum ) : state.m2 );
43
+
44
+ if ( state.variance < 0 ) state.variance = 0;
45
+ state.stdev = Math.sqrt( state.variance );
46
+
47
+ if ( state.stats.snrDB ) {
48
+ if ( state.stdev < state.EPS ) {
49
+ state.snrDB = state.snrDbCap; // No noise — clean signal → 60 dB cap
50
+ } else if ( Math.abs( state.mean ) < state.EPS ) {
51
+ state.snrDB = 0; // No signal — noise dominates → 0 dB
52
+ } else {
53
+ state.snrDB = 20 * Math.log10( Math.abs( state.mean ) / state.stdev );
54
+ }
55
+ }
56
+
57
+ if ( state.stats.cv ) {
58
+ state.cv = ( Math.abs( state.mean ) > state.EPS ) ?
59
+ ( state.stdev / Math.abs( state.mean ) ) : state.cvLarge;
60
+ }
61
+ }; // updateWelford()
62
+
63
+ export default updateWelford;
64
+
@@ -0,0 +1,59 @@
1
+ /**
2
+ * @fileoverview Hot-path dispatcher for the esStats node.
3
+ *
4
+ * Routes incoming values through first-sample initialization (seeds mean,
5
+ * floor, ceiling), then delegates to updateWelford (mean, variance, SNR,
6
+ * CV, zScore) and updateEnvelope (floor, ceiling, envScore) based on
7
+ * computation-path flags resolved at init. Zero allocations; all
8
+ * reads/writes via the pre-allocated state object.
9
+ */
10
+ // nodes/es-stats/update.js
11
+
12
+ import updateEnvelope from './update-envelope.js';
13
+ import updateWelford from './update-welford.js';
14
+
15
+ /**
16
+ * Update all requested statistics in a single pass.
17
+ * Updates statistics using Welford's online algorithm
18
+ * for numerical stability.
19
+ *
20
+ * @param {Object} state - Node state
21
+ * @param {Object} msg - Message containing input value
22
+ * @returns {Object} Updated state
23
+ */
24
+ const update = function ( state, msg ) {
25
+ if ( state.disable || state.pause ) return state;
26
+ const xVal = msg[ state.x ];
27
+ // Reset on each update
28
+ state.inputValidationFailed = false;
29
+ // Handle faults gracefully: ensure their isolation
30
+ if ( !Number.isFinite( xVal ) ) {
31
+ // Signals publishing NaN for all demanded `stats` in publishTo.
32
+ state.inputValidationFailed = true;
33
+ return state;
34
+ }
35
+
36
+ // First sample initialization
37
+ if ( state.sampleCount === 0 ) {
38
+ state.mean = xVal;
39
+ state.m2 = 0;
40
+ state.variance = 0;
41
+ state.stdev = 0;
42
+ state.floor = xVal;
43
+ state.ceiling = xVal;
44
+ state.envelope = 0;
45
+ state.mid = xVal;
46
+ state.weightSum = state.alpha;
47
+ state.sampleCount = 1;
48
+ return state;
49
+ }
50
+
51
+ state.sampleCount += 1;
52
+
53
+ if ( state.needsWelford ) updateWelford( state, xVal );
54
+ if ( state.needsEnvelope ) updateEnvelope( state, xVal );
55
+
56
+ return state;
57
+ }; // update()
58
+
59
+ export default update;
@@ -0,0 +1,83 @@
1
+ // nodes/index.js
2
+
3
+ // Import all nodes
4
+ import * as accumulate from './accumulate/index.js';
5
+ import * as appraise from './appraise/index.js';
6
+ import * as butterworthFilter from './butterworth-filter/index.js';
7
+ import * as categorize from './categorize/index.js';
8
+ import * as controller from './controller/index.js';
9
+ import * as diff from './diff/index.js';
10
+ import * as lag from './lag/index.js';
11
+ import * as digestMoments from './digest-moments/index.js';
12
+ import * as dwellTimeTracker from './dwell-time-tracker/index.js';
13
+ import * as emitIf from './emit-if/index.js';
14
+ import * as esMean from './es-mean/index.js';
15
+ import * as invertFlag from './invert-flag/index.js';
16
+ import * as kalman1d from './kalman1d/index.js';
17
+ import * as esCorrelation from './es-correlation/index.js';
18
+ import * as esPairwiseCorrelation from './es-pairwise-correlation/index.js';
19
+ import * as esStats from './es-stats/index.js';
20
+ import * as kernel from './kernel/index.js';
21
+ import * as median3 from './median3/index.js';
22
+ import * as pageHinkley from './page-hinkley/index.js';
23
+ import * as passIf from './pass-if/index.js';
24
+ import * as persistIf from './persist-if/index.js';
25
+ import * as persistenceCheck from './persistence-check/index.js';
26
+ import * as processIndex from './process-index/index.js';
27
+ import * as ratio from './ratio/index.js';
28
+ import * as sanitize from './sanitize/index.js';
29
+ import * as spikeGuard from './spike-guard/index.js';
30
+ import * as stateChangeDetector from './state-change-detector/index.js';
31
+ import * as momentsDigest from './moments-digest/index.js';
32
+ import * as swStats from './sw-stats/index.js';
33
+ import * as swingWatch from './swing-watch/index.js';
34
+ import * as tally from './tally/index.js';
35
+ import * as threshold from './threshold/index.js';
36
+ import * as transform from './transform/index.js';
37
+ import * as trend from './trend/index.js';
38
+ import * as twStats from './tw-stats/index.js';
39
+ import * as unbalance from './unbalance/index.js';
40
+ import * as vectorDistance from './vector-distance/index.js';
41
+ import * as winnow from './winnow/index.js';
42
+
43
+
44
+ export {
45
+ accumulate,
46
+ appraise,
47
+ butterworthFilter,
48
+ categorize,
49
+ controller,
50
+ diff,
51
+ digestMoments,
52
+ dwellTimeTracker,
53
+ emitIf,
54
+ esMean,
55
+ esCorrelation,
56
+ esPairwiseCorrelation,
57
+ esStats,
58
+ invertFlag,
59
+ kalman1d,
60
+ kernel,
61
+ lag,
62
+ median3,
63
+ pageHinkley,
64
+ passIf,
65
+ persistIf,
66
+ persistenceCheck,
67
+ processIndex,
68
+ ratio,
69
+ sanitize,
70
+ spikeGuard,
71
+ stateChangeDetector,
72
+ momentsDigest,
73
+ swStats,
74
+ swingWatch,
75
+ tally,
76
+ threshold,
77
+ transform,
78
+ trend,
79
+ twStats,
80
+ unbalance,
81
+ vectorDistance,
82
+ winnow
83
+ };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @fileoverview Entry point for invertFlag 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';
20
+
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @fileoverview Initialization for invertFlag node.
3
+ *
4
+ * Validates the spec, extracts the input field name, and returns
5
+ * the fully initialized state object.
6
+ */
7
+
8
+ import { validateSpec } from '../../core/utils/node/index.js';
9
+ import * as introspect from './introspect.js';
10
+
11
+ const init = function ( spec ) {
12
+ validateSpec( spec, introspect );
13
+
14
+ const state = Object.create( null );
15
+ // Standard input parameter health flag during update: false means healthy
16
+ state.inputValidationFailed = false;
17
+ // Ensure node is enabled by default
18
+ state.disable = false;
19
+ state.pause = false;
20
+
21
+ // Extract field name
22
+ state.x = spec.from.x;
23
+
24
+ // Config copy
25
+ state.stats = spec.stats;
26
+
27
+ // State variable
28
+ state.inverted = false;
29
+
30
+ state.nodeType = introspect.getNodeType();
31
+
32
+ return state;
33
+ }; // init()
34
+
35
+ export default init;
36
+
@@ -0,0 +1,115 @@
1
+ // nodes/invert-flag/introspect.js
2
+
3
+ /**
4
+ * @fileoverview Introspection metadata for invertFlag node.
5
+ *
6
+ * Inverts a boolean field (e.g., dwell emits active=false on transition,
7
+ * dashboard shows "wasActive=true").
8
+ *
9
+ * DSL: .invertFlag( 'invert', 'active', { inverted: { storeAs: 'wasActive' } } )
10
+ */
11
+
12
+ import { validators } from '../../core/utils/validate/index.js';
13
+
14
+ /** Single source of truth for what this plugin can do */
15
+ const SUPPORTED_STATS = [
16
+ 'inverted'
17
+ ];
18
+
19
+ /** Human-readable descriptions (for introspection) */
20
+ const STAT_DESCRIPTIONS = {
21
+ inverted: 'Inverted boolean value (!x)',
22
+ };
23
+
24
+ /** Control methods available for triggers */
25
+ const SUPPORTED_CONTROL_METHODS = {
26
+ reset: 'Clears accumulated state and restarts computation',
27
+ enable: 'Enables node processing',
28
+ disable: 'Disables node processing',
29
+ pause: 'Pauses node processing while keeping state visible',
30
+ unpause: 'Resumes node processing after pause'
31
+ };
32
+
33
+ /** The type of this node */
34
+ const NODE_TYPE = 'Invert Flag';
35
+
36
+ /** Node capabilities for documentation */
37
+ const CAPABILITIES = {
38
+ description: 'Inverts a boolean field in the message',
39
+ features: [
40
+ 'Boolean inversion: true → false, false → true',
41
+ 'Handles truthy/falsy values via JavaScript coercion',
42
+ 'Useful for inverting state transition flags'
43
+ ]
44
+ };
45
+
46
+ // Attach static metadata once, from our constants by making a shallow copy.
47
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
48
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
49
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
50
+ export const getNodeType = () => NODE_TYPE;
51
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
52
+
53
+ export const DEFAULT_OPTIONS = Object.create( null );
54
+
55
+ // DSL Metadata for transpilation
56
+ const DSL_METADATA = {
57
+ // Spec schema for validation
58
+ specSchema: {
59
+ nodeType: {
60
+ type: 'string',
61
+ required: true,
62
+ value: NODE_TYPE
63
+ },
64
+ name: {
65
+ type: 'string',
66
+ required: true,
67
+ validator: validators.identifier,
68
+ error: 'Name must be a valid identifier'
69
+ },
70
+ from: {
71
+ type: 'object',
72
+ required: true,
73
+ properties: {
74
+ x: {
75
+ type: 'string',
76
+ required: true,
77
+ validator: validators.noSpaces,
78
+ error: 'Field name cannot contain spaces'
79
+ }
80
+ }
81
+ },
82
+ stats: {
83
+ type: 'object',
84
+ required: true,
85
+ propertyNames: SUPPORTED_STATS,
86
+ propertySchema: {
87
+ type: 'object',
88
+ required: true,
89
+ properties: {
90
+ storeAs: {
91
+ type: 'string',
92
+ required: true,
93
+ minLength: 1,
94
+ validator: validators.identifier,
95
+ error: 'storeAs must be a valid identifier'
96
+ }
97
+ }
98
+ }
99
+ }
100
+ },
101
+
102
+ // How to build the spec from DSL params
103
+ // DSL: .invertFlag( 'myInvert', 'active', { inverted: { storeAs: 'wasActive' } } )
104
+ buildSpec: ( name, x, stats, options ) => ( {
105
+ nodeType: NODE_TYPE,
106
+ name,
107
+ from: { x },
108
+ stats,
109
+ ...options
110
+ } ),
111
+
112
+ };
113
+
114
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
115
+
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Publish function for invertFlag node.
3
+ *
4
+ * Copies the inverted flag to the output message, or propagates
5
+ * NaN if the last 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
+
13
+ // Propagate NaN if unhealthy
14
+ if ( state.inputValidationFailed ) {
15
+ publishNaN( state, msg );
16
+ return;
17
+ }
18
+
19
+ msg[ state.stats.inverted.storeAs ] = state.inverted;
20
+ }; // publishTo()
21
+
22
+ export default publishTo;
23
+
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Recompute for invertFlag node.
3
+ *
4
+ * No-op: inversion is computed fresh from raw input on every update
5
+ * — there is no accumulated state that can drift over time.
6
+ */
7
+
8
+ const recompute = function () {
9
+ return true;
10
+ }; // recompute()
11
+
12
+ export default recompute;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Reset invertFlag node state.
3
+ *
4
+ * Clears the inverted flag back to its initial value.
5
+ */
6
+
7
+ const reset = function ( state ) {
8
+ state.inverted = false;
9
+ return true;
10
+ }; // reset()
11
+
12
+ export default reset;
@@ -0,0 +1,32 @@
1
+ // nodes/invert-flag/update.js
2
+
3
+ /**
4
+ * @fileoverview Update function for invertFlag node.
5
+ *
6
+ * Inverts a boolean field using JavaScript's ! operator.
7
+ * Accepts boolean and truthy/falsy values (e.g., 0/1).
8
+ */
9
+
10
+ const update = function ( state, msg ) {
11
+ if ( state.disable || state.pause ) return state;
12
+
13
+ // Reset on each update
14
+ state.inputValidationFailed = false;
15
+
16
+ const value = msg[ state.x ];
17
+
18
+ // Handle faults gracefully: null/undefined signals missing data
19
+ if ( value === undefined || value === null ) {
20
+ state.inputValidationFailed = true;
21
+ return state;
22
+ }
23
+
24
+ // Invert the value using JavaScript's ! operator
25
+ // Works with booleans (true/false) and truthy/falsy values (1/0)
26
+ state.inverted = !value;
27
+
28
+ return state;
29
+ }; // update()
30
+
31
+ export default update;
32
+
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @fileoverview Public API barrel for kalman1d node.
3
+ *
4
+ * Re-exports all standard node interface functions and introspection metadata.
5
+ * The kalman1d node implements a 1-D Kalman filter for model-based state
6
+ * estimation with control inputs and statistical outlier detection — the
7
+ * foundation for context-aware monitoring and data-driven digital twin
8
+ * capability in Composer pipelines.
9
+ */
10
+
11
+ export { default as init } from './init.js';
12
+ export { default as update } from './update.js';
13
+ export { default as reset } from './reset.js';
14
+ export { default as recompute } from './recompute.js';
15
+ export { default as publishTo } from './publish-to.js';
16
+ export { default as disable } from '../../core/utils/node/disable.js';
17
+ export { default as enable } from '../../core/utils/node/enable.js';
18
+ export { default as pause } from '../../core/utils/node/pause.js';
19
+ export { default as unpause } from '../../core/utils/node/unpause.js';
20
+ export * from './introspect.js';
@@ -0,0 +1,103 @@
1
+ /**
2
+ * @fileoverview Initialization for kalman1d node.
3
+ *
4
+ * Validates the spec, resolves field-keyed parameters, computes derived
5
+ * Kalman coefficients, and returns fully initialized state.
6
+ *
7
+ * **Q and R parameterization:**
8
+ * `processVariance` is the process noise variance Q in absolute units
9
+ * (state²). `sensorVariance` is the measurement noise variance R in
10
+ * absolute units (measurement²). Both are independent — neither is
11
+ * computed from the other. This matches the textbook 1-D Kalman filter
12
+ * (Bar-Shalom, Welch & Bishop) and the upstream `winkjs/wink-statistics/
13
+ * src/streaming-1d-kalman-filter.js` from which this node was ported.
14
+ *
15
+ * **Auto-initialization:**
16
+ * State starts uninitialized (isInitialized = false). The first valid
17
+ * measurement sets xHat = z/H and P = R/H², bootstrapping the filter
18
+ * without requiring a prior estimate. Innovation is 0 on the first tick
19
+ * (no prediction to compare against).
20
+ *
21
+ * **Variance floor (Pmin):**
22
+ * Prevents P from collapsing to zero, which would cause the filter to
23
+ * ignore all future measurements — a known Kalman failure mode called
24
+ * "filter lock." Pmin = 1e-10 × R is small enough to never affect
25
+ * normal operation but catches the degenerate case.
26
+ */
27
+
28
+ import { validateSpec } from '../../core/utils/node/index.js';
29
+ import * as introspect from './introspect.js';
30
+ import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
31
+
32
+ const init = function ( spec ) {
33
+ validateSpec( spec, introspect );
34
+
35
+ const state = Object.create( null );
36
+
37
+ // ── Standard flags (all nodes) ──────────────────────────────────────
38
+ state.inputValidationFailed = false;
39
+ state.disable = false;
40
+ state.pause = false;
41
+
42
+ // ── Input field ─────────────────────────────────────────────────────
43
+ state.x = spec.from.x;
44
+
45
+ // ── Control input (optional) ────────────────────────────────────────
46
+ // Any causal influence: heater power → temperature, fuel rate → tank
47
+ // level, motor current → bearing temperature. When absent, the
48
+ // prediction step simplifies to xPred = F·xHat (zero-control Kalman).
49
+ state.controlField = spec.control ?? null;
50
+
51
+ // ── Resolve field-keyed tunables (per-sensor differentiation) ────────
52
+ const sensorVarianceSpec = resolveScalar( spec.sensorVariance, state.x );
53
+ const processVarianceSpec = resolveScalar( spec.processVariance, state.x );
54
+ const chi2ThresholdSpec = resolveScalar( spec.chi2Threshold, state.x );
55
+ const controlModelSpec = resolveScalar( spec.controlModel, state.x );
56
+
57
+ // ── Apply defaults ──────────────────────────────────────────────────
58
+ const R = sensorVarianceSpec ?? introspect.DEFAULT_OPTIONS.sensorVariance;
59
+ const Q = processVarianceSpec ?? introspect.DEFAULT_OPTIONS.processVariance;
60
+
61
+ // ── Core Kalman parameters ──────────────────────────────────────────
62
+ // State-space model: x(k+1) = F·x(k) + G·u(k) + w, z(k) = H·x(k) + v
63
+ // Q (process noise variance) and R (measurement noise variance) are
64
+ // independent absolute values. Q is in state² units, R in measurement².
65
+ state.R = R;
66
+ state.Q = Q;
67
+ state.F = spec.stateTransition ?? introspect.DEFAULT_OPTIONS.stateTransition;
68
+ state.G = controlModelSpec ?? introspect.DEFAULT_OPTIONS.controlModel;
69
+ state.H = spec.measurement ?? introspect.DEFAULT_OPTIONS.measurement;
70
+
71
+ // ── Outlier detection ───────────────────────────────────────────────
72
+ // Innovation gate: chi-squared(1) test on (innovation²/S).
73
+ // 6.63 = 99% confidence → 1% false alarm rate.
74
+ state.chi2Threshold = chi2ThresholdSpec ?? introspect.DEFAULT_OPTIONS.chi2Threshold;
75
+ state.followMode = spec.followMode ?? introspect.DEFAULT_OPTIONS.followMode;
76
+
77
+ // ── Numerical safeguards ────────────────────────────────────────────
78
+ const varLimit = spec.varianceLimit ?? introspect.DEFAULT_OPTIONS.varianceLimit;
79
+ state.Pmax = varLimit * R;
80
+ state.Pmin = 1e-10 * R;
81
+
82
+ // ── State variables (pre-first-measurement) ─────────────────────────
83
+ state.xHat = 0;
84
+ state.P = 0;
85
+ state.isInitialized = false;
86
+
87
+ // ── Innovation outputs ──────────────────────────────────────────────
88
+ // Always available, even on excluded outlier measurements.
89
+ state.innovation = 0;
90
+ state.innovationGate = 0;
91
+
92
+ // ── Diagnostics ─────────────────────────────────────────────────────
93
+ state.updateCount = 0;
94
+ state.outlierCount = 0;
95
+
96
+ // ── Output configuration ────────────────────────────────────────────
97
+ state.stats = spec.stats;
98
+ state.nodeType = introspect.getNodeType();
99
+
100
+ return state;
101
+ }; // init()
102
+
103
+ export default init;