@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,39 @@
1
+ /**
2
+ * @fileoverview Pre-built transform helpers — pure math primitives.
3
+ *
4
+ * Each helper is a single-argument pure function suitable for the
5
+ * transform node's `using` option. Helpers carry a `.semantics`
6
+ * property for introspection, consistent with tunable helpers.
7
+ *
8
+ * @example
9
+ * import { square, abs } from '@winkjs/composer';
10
+ * .transform( 'sq', 'ecgDeriv', { result: 'ecgSquared' }, { using: square } )
11
+ */
12
+
13
+ /** Squares the input: x * x */
14
+ export const square = ( x ) => ( x * x );
15
+ square.semantics = { type: 'transform', name: 'square', formula: 'x * x' };
16
+
17
+ /** Absolute value: |x| */
18
+ export const abs = ( x ) => Math.abs( x );
19
+ abs.semantics = { type: 'transform', name: 'abs', formula: '|x|' };
20
+
21
+ /** Square root: √x (NaN if x < 0) */
22
+ export const sqrt = ( x ) => Math.sqrt( x );
23
+ sqrt.semantics = { type: 'transform', name: 'sqrt', formula: '√x' };
24
+
25
+ /** Natural logarithm: ln(x) (-Infinity if x = 0, NaN if x < 0) */
26
+ export const log = ( x ) => Math.log( x );
27
+ log.semantics = { type: 'transform', name: 'log', formula: 'ln(x)' };
28
+
29
+ /** Base-10 logarithm: log₁₀(x) (-Infinity if x = 0, NaN if x < 0) */
30
+ export const log10 = ( x ) => Math.log10( x );
31
+ log10.semantics = { type: 'transform', name: 'log10', formula: 'log₁₀(x)' };
32
+
33
+ /** Reciprocal: 1/x (Infinity if x = 0) */
34
+ export const reciprocal = ( x ) => ( 1 / x );
35
+ reciprocal.semantics = { type: 'transform', name: 'reciprocal', formula: '1/x' };
36
+
37
+ /** Negation: -x */
38
+ export const negate = ( x ) => ( -x );
39
+ negate.semantics = { type: 'transform', name: 'negate', formula: '-x' };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @fileoverview Transform node — applies a user-supplied pure function
3
+ * to each sample: result = using( x ).
4
+ */
5
+
6
+ export { default as init } from './init.js';
7
+ export { default as update } from './update.js';
8
+ export { default as reset } from './reset.js';
9
+ export { default as recompute } from './recompute.js';
10
+ export { default as publishTo } from './publish-to.js';
11
+ export { default as disable } from '../../core/utils/node/disable.js';
12
+ export { default as enable } from '../../core/utils/node/enable.js';
13
+ export { default as pause } from '../../core/utils/node/pause.js';
14
+ export { default as unpause } from '../../core/utils/node/unpause.js';
15
+ export * from './introspect.js';
@@ -0,0 +1,35 @@
1
+ /**
2
+ * @fileoverview Initialization for the transform node.
3
+ *
4
+ * Validates the spec and creates a minimal state object. The user-supplied
5
+ * function (`using`) is stored as a direct reference — it is fixed at init
6
+ * time and not wrapped as a tunable.
7
+ */
8
+
9
+ import { validateSpec } from '../../core/utils/node/index.js';
10
+ import * as introspect from './introspect.js';
11
+
12
+ const init = function ( spec ) {
13
+ validateSpec( spec, introspect );
14
+
15
+ const state = Object.create( null );
16
+
17
+ // Standard flags
18
+ state.inputValidationFailed = false;
19
+ state.disable = false;
20
+ state.pause = false;
21
+
22
+ // Configuration from spec
23
+ state.x = spec.from.x;
24
+ state.stats = spec.stats;
25
+ state.using = spec.using;
26
+
27
+ // Pre-allocate output slot
28
+ state.result = 0;
29
+
30
+ state.nodeType = introspect.getNodeType();
31
+
32
+ return state;
33
+ }; // init()
34
+
35
+ export default init;
@@ -0,0 +1,118 @@
1
+ /**
2
+ * @fileoverview Introspection metadata for the transform node.
3
+ *
4
+ * Applies a user-supplied pure function to each sample: result = using( x ).
5
+ * The function is fixed at init time and receives a single numeric value.
6
+ * Transform-produced NaN (e.g. sqrt of negative) flows naturally downstream;
7
+ * only genuine input validation failures set the health flag.
8
+ *
9
+ * DSL: .transform( 'sq', 'ecgDeriv', { result: 'ecgSquared' }, { using: square } )
10
+ */
11
+
12
+ import { validators } from '../../core/utils/validate/index.js';
13
+
14
+ // ── Supported Features ──────────────────────────────────────────
15
+
16
+ const SUPPORTED_STATS = [ 'result' ];
17
+
18
+ const STAT_DESCRIPTIONS = {
19
+ result: 'Transformed value: using(x)'
20
+ };
21
+
22
+ const SUPPORTED_CONTROL_METHODS = {
23
+ reset: 'Clears accumulated state and restarts computation',
24
+ enable: 'Enables node processing',
25
+ disable: 'Disables node processing',
26
+ pause: 'Pauses node processing while keeping state visible',
27
+ unpause: 'Resumes node processing after pause'
28
+ };
29
+
30
+ const NODE_TYPE = 'Transform';
31
+
32
+ const CAPABILITIES = {
33
+ description: 'Applies a user-supplied pure function to each sample',
34
+ features: [
35
+ 'Applies a pure function to each sample: result = using(x)',
36
+ 'Validates input for NaN/Infinity; transform-produced NaN flows naturally downstream',
37
+ 'Ships with pre-built helpers: square, abs, sqrt, log, log10, reciprocal, negate'
38
+ ]
39
+ };
40
+
41
+ // ── Default Options ─────────────────────────────────────────────
42
+
43
+ export const DEFAULT_OPTIONS = Object.create( null );
44
+
45
+ // ── Getter Functions (defensive copies) ─────────────────────────
46
+
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 = () => ( {
52
+ ...CAPABILITIES,
53
+ features: CAPABILITIES.features.slice()
54
+ } );
55
+
56
+ // ── DSL Metadata ────────────────────────────────────────────────
57
+
58
+ const DSL_METADATA = {
59
+ specSchema: {
60
+ nodeType: {
61
+ type: 'string',
62
+ required: true,
63
+ value: NODE_TYPE
64
+ },
65
+ name: {
66
+ type: 'string',
67
+ required: true,
68
+ validator: validators.identifier,
69
+ error: 'Name must be a valid identifier'
70
+ },
71
+ from: {
72
+ type: 'object',
73
+ required: true,
74
+ properties: {
75
+ x: {
76
+ type: 'string',
77
+ required: true,
78
+ validator: validators.noSpaces,
79
+ error: 'Field name cannot contain spaces'
80
+ }
81
+ }
82
+ },
83
+ stats: {
84
+ type: 'object',
85
+ required: true,
86
+ propertyNames: SUPPORTED_STATS,
87
+ propertySchema: {
88
+ type: 'object',
89
+ required: true,
90
+ properties: {
91
+ storeAs: {
92
+ type: 'string',
93
+ required: true,
94
+ minLength: 1,
95
+ validator: validators.identifier,
96
+ error: 'storeAs must be a valid identifier'
97
+ }
98
+ }
99
+ }
100
+ },
101
+ using: {
102
+ type: 'function',
103
+ required: true,
104
+ arity: 1,
105
+ error: '"using" must be a pure function with exactly one parameter'
106
+ }
107
+ },
108
+
109
+ buildSpec: ( name, x, stats, options ) => ( {
110
+ nodeType: NODE_TYPE,
111
+ name,
112
+ from: { x },
113
+ stats,
114
+ ...options
115
+ } )
116
+ };
117
+
118
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Publish-to function for the transform node.
3
+ *
4
+ * Copies the computed result to the output message. When input
5
+ * validation failed, publishes NaN via the standard helper.
6
+ * When the transform produced NaN (e.g. sqrt of negative),
7
+ * the NaN flows through naturally — no flag, no special handling.
8
+ */
9
+
10
+ import { publishNaN } from '../../core/utils/node/index.js';
11
+
12
+ const publishTo = function ( state, msg ) {
13
+ if ( state.disable ) return;
14
+
15
+ if ( state.inputValidationFailed ) {
16
+ publishNaN( state, msg );
17
+ return;
18
+ }
19
+
20
+ msg[ state.stats.result.storeAs ] = state.result;
21
+ }; // publishTo()
22
+
23
+ export default publishTo;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Recompute function for the transform node.
3
+ *
4
+ * No numerical stability concerns — the node is stateless with
5
+ * no accumulators, running sums, or recursive filters.
6
+ */
7
+
8
+ const recompute = function () {
9
+ return true;
10
+ }; // recompute()
11
+
12
+ export default recompute;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @fileoverview Reset function for the transform node.
3
+ *
4
+ * No accumulated state to clear — the node is stateless.
5
+ */
6
+
7
+ const reset = function () {
8
+ return true;
9
+ }; // reset()
10
+
11
+ export default reset;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @fileoverview Update function for the transform node.
3
+ *
4
+ * Applies the user-supplied pure function to the input value.
5
+ * Input validation uses the standard `inputValidationFailed` flag.
6
+ * Transform-produced NaN (e.g. sqrt of negative) is NOT flagged —
7
+ * the node is healthy; the math produced NaN. Downstream nodes
8
+ * catch it via their own input validation.
9
+ */
10
+
11
+ const update = function ( state, msg ) {
12
+ if ( state.disable || state.pause ) return state;
13
+
14
+ state.inputValidationFailed = false;
15
+
16
+ const xVal = msg[ state.x ];
17
+
18
+ if ( !Number.isFinite( xVal ) ) {
19
+ state.inputValidationFailed = true;
20
+ return state;
21
+ }
22
+
23
+ try {
24
+ state.result = state.using( xVal );
25
+ } catch {
26
+ state.result = NaN;
27
+ }
28
+
29
+ return state;
30
+ }; // update()
31
+
32
+ export default update;
@@ -0,0 +1,112 @@
1
+ // nodes/trend/compute-confidence.js
2
+
3
+ const computeConfidence = function ( state ) {
4
+ // During warmup: linear ramp from 0 to 1 based on sample count
5
+ // Provides gradual trust building as statistics stabilize
6
+ if ( state.samples < state.warmupSamples ) {
7
+ const ramp = state.samples / Math.max( state.warmupSamples, 1 );
8
+ return ( ramp < 0 ) ? 0 : ( ramp > 1 ? 1 : ramp );
9
+ }
10
+
11
+ const epsilon = state.epsilon ?? 1e-12;
12
+
13
+ // Prevent division by zero while preserving user's threshold intent
14
+ // If threshold is 0, user wants to detect ANY change, so use tiny epsilon
15
+ const safeThreshold = ( state.rocThreshold > 0 ) ? state.rocThreshold : epsilon;
16
+
17
+ // Compute noise level (standard deviation) with numerical safety
18
+ // Variance can be 0 for perfectly stable signals or negative due to numerical errors
19
+ const rawStd = Math.sqrt( Math.max( state.rocVariance, 0 ) );
20
+ const stddev = ( rawStd > epsilon ) ? rawStd : epsilon;
21
+
22
+ // Persistence: rewards consistent state classification
23
+ // tanh(x/5) gives: 0.2 at 1 sample, 0.76 at 5, 0.999 at 20
24
+ const persistence = Math.tanh( state.consistentSamples / 5 );
25
+ const persistenceFactor = ( 0.5 + ( 0.5 * persistence ) );
26
+ const absRoCMean = Math.abs( state.rocMean );
27
+
28
+ if ( state.trend === 'stable' ) {
29
+ // STABLE CONFIDENCE: "How confident are we that this is truly stable?"
30
+
31
+ // Quietness factor: confidence that noise won't cause false trends
32
+ // - When stddev << threshold: q ≈ 0, quietness ≈ 1 (high confidence)
33
+ // - When stddev ≈ threshold: q ≈ 1, quietness ≈ 0.5 (uncertain)
34
+ // - When stddev >> threshold: q >> 1, quietness ≈ 0 (no confidence)
35
+ // Quadratic denominator provides smooth, intuitive decay
36
+ const q = stddev / safeThreshold;
37
+ const quietness = 1 / ( 1 + ( q * q ) );
38
+
39
+ // Margin factor: confidence we're not near the threshold boundary
40
+ // - At roc = 0: margin = 1 (center of stable zone)
41
+ // - At roc = threshold: margin = 0 (edge of stability)
42
+ // This prevents high confidence when hovering near transition
43
+ const r = absRoCMean / safeThreshold;
44
+ const margin = 1 - Math.min( 1, r );
45
+
46
+ // Combine factors: far from edge AND low noise AND persistent = high confidence
47
+ const confStable = margin * quietness * persistenceFactor;
48
+ return ( confStable < 0 ) ? 0 : ( confStable > 1 ? 1 : confStable );
49
+ }
50
+
51
+ // TRENDING CONFIDENCE: "How confident are we in this trend direction?"
52
+
53
+ // Signal clarity: signal-to-noise ratio of the *mean roc estimate*.
54
+ //
55
+ // The per-sample SNR (|rocMean| / stddev) answers: "is each individual
56
+ // roc sample clearly positive?" But the relevant question for trend
57
+ // confidence is: "given all the samples I've seen, is the *average*
58
+ // roc clearly positive?"
59
+ //
60
+ // The standard error of the EWMA mean is stddev / sqrt(n_eff), where
61
+ // n_eff ≈ (2 / alpha) - 1 is the effective sample size of the EWMA.
62
+ // Scaling by sqrt(n_eff) converts per-sample SNR to mean-estimate SNR.
63
+ //
64
+ // Example: per-sample SNR = 0.5 with n_eff = 35 gives
65
+ // SNR_mean = 0.5 × sqrt(35) ≈ 2.96 → signalClarity ≈ 0.76
66
+ // vs. signalClarity ≈ 0.16 without the correction.
67
+ //
68
+ // Cap n_eff at actual post-warmup samples to avoid over-confidence early.
69
+ const nEff = Math.min( ( 2 / state.rocAlpha ) - 1, state.samples - state.warmupSamples );
70
+ const safeNEff = ( nEff > 1 ) ? nEff : 1;
71
+ state.snr = ( absRoCMean / stddev ) * Math.sqrt( safeNEff );
72
+ const signalClarity = Math.tanh( state.snr / 3 );
73
+
74
+ // Boundary clarity: how far above the classification threshold?
75
+ // - Just above (roc ≈ 1.1×threshold): overshoot ≈ 0.1, clarity ≈ 0.1
76
+ // - Well above (roc = 2×threshold): overshoot = 1, clarity ≈ 0.76
77
+ // - Far above (roc = 5×threshold): overshoot = 4, clarity ≈ 0.999
78
+ // Using threshold-normalized ratio makes this parameter-agnostic
79
+ const overshoot = Math.max( 0, ( absRoCMean / safeThreshold ) - 1 );
80
+ const boundaryClarity = Math.tanh( overshoot );
81
+
82
+ // For trending states, we intentionally DO NOT apply the quietness penalty.
83
+ // Here's why:
84
+ //
85
+ // The quietness factor (1/(1+q²) where q = stddev/threshold) was designed to
86
+ // penalize high noise relative to the threshold. This makes sense for STABLE
87
+ // states where we want confidence that noise won't cause false trend detection.
88
+ //
89
+ // However, for TRENDING states, applying quietness creates redundant penalties:
90
+ // 1. signalClarity already accounts for noise via SNR (signal/noise ratio)
91
+ // 2. boundaryClarity ensures we're sufficiently above the threshold
92
+ //
93
+ // Adding quietness on top would triple-penalize for noise, causing even strong
94
+ // trends (SNR > 20) to have low confidence if stddev happens to be comparable
95
+ // to the threshold. This is overly conservative.
96
+ //
97
+ // Example: A perfect linear trend with roc=1.0, threshold=0.1, stddev=0.15
98
+ // - SNR = 6.7 (excellent signal!)
99
+ // - boundaryClarity = tanh(9) ≈ 1.0 (far above threshold!)
100
+ // - But quietness = 1/(1+1.5²) = 0.31 (harsh penalty)
101
+ // - Result: confidence = 0.31 instead of ~0.9
102
+ //
103
+ // For trending states, if the SNR is high and we're well above threshold,
104
+ // the absolute noise level is less relevant - the trend is clearly real.
105
+ // Therefore, we omit the quietness factor for rising/falling states.
106
+ const confTrend = signalClarity * boundaryClarity * persistenceFactor;
107
+
108
+ // Ensure output is strictly in [0,1] despite numerical errors
109
+ return ( confTrend < 0 ) ? 0 : ( confTrend > 1 ? 1 : confTrend );
110
+ }; // computeConfidence()
111
+
112
+ export { computeConfidence };
@@ -0,0 +1,14 @@
1
+ // nodes/trend/index.js
2
+
3
+ export { default as init } from './init.js';
4
+ export { default as update } from './update.js';
5
+ export { default as reset } from './reset.js';
6
+ export { default as recompute } from './recompute.js';
7
+ export { default as publishTo } from './publish-to.js';
8
+ // Direct imports for common control functions
9
+ export { default as disable } from '../../core/utils/node/disable.js';
10
+ export { default as enable } from '../../core/utils/node/enable.js';
11
+ export { default as pause } from '../../core/utils/node/pause.js';
12
+ export { default as unpause } from '../../core/utils/node/unpause.js';
13
+ // Re-export everything from introspection.js
14
+ export * from './introspect.js';
@@ -0,0 +1,81 @@
1
+ // nodes/trend/init.js
2
+
3
+ import { validateWithSchema } from '../../core/utils/validate/index.js';
4
+ import { getDSLMetadata, getNodeType, DEFAULT_OPTIONS } from './introspect.js';
5
+ import { halfLifeToAlpha, halfLifeToWarmupSamples } from '../../core/utils/half-life/index.js';
6
+ import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
7
+ import { asTunable } from '../../core/tunable/index.js';
8
+
9
+ const init = function ( spec ) {
10
+ // Validate specification
11
+ const metadata = getDSLMetadata();
12
+ const validation = validateWithSchema(
13
+ {
14
+ ...metadata.specSchema,
15
+ _crossFieldValidators: metadata.crossFieldValidators
16
+ },
17
+ spec,
18
+ 'spec'
19
+ );
20
+ validation.throwIfInvalid( getNodeType() );
21
+
22
+ // Create state after validation passes
23
+ const state = Object.create( null );
24
+ // Standard input parameter health flag during update: false means healthy
25
+ state.inputValidationFailed = false;
26
+ // Ensure node is enabled by default
27
+ state.disable = false;
28
+ state.pause = false;
29
+
30
+ // Core configuration
31
+ state.x = spec.from.x;
32
+ state.stats = spec.stats;
33
+
34
+ // Apply parameters with defaults (validation doesn't enforce them)
35
+ // Supports direct, field-keyed, and tunable specification for tunable params
36
+ const rocStatsHalfLifeSpec = resolveScalar( spec.rocStatsHalfLife, state.x );
37
+ const rocThresholdSpec = resolveScalar( spec.rocThreshold, state.x );
38
+ const speedUpSpec = resolveScalar( spec.speedUp, state.x );
39
+ const warmupSamplesSpec = resolveScalar( spec.warmupSamples, state.x );
40
+
41
+ // Structural params (affect derived values at init time)
42
+ state.rocStatsHalfLife = rocStatsHalfLifeSpec ?? DEFAULT_OPTIONS.rocStatsHalfLife;
43
+ state.rocAlpha = halfLifeToAlpha( state.rocStatsHalfLife );
44
+ state.speedUp = speedUpSpec ?? DEFAULT_OPTIONS.speedUp;
45
+ state.warmupSamples = warmupSamplesSpec ?? halfLifeToWarmupSamples( state.rocStatsHalfLife, 0.8 );
46
+ state.warmupSamplesForAccel = Math.ceil( state.warmupSamples * 1.5 );
47
+
48
+ // rocThreshold supports tunable for phase-dependent sensitivity
49
+ state.rocThresholdFn = asTunable( rocThresholdSpec ?? DEFAULT_OPTIONS.rocThreshold );
50
+ state.rocThreshold = DEFAULT_OPTIONS.rocThreshold;
51
+ state.tunableErrorLogged = false;
52
+
53
+ state.previousValue = null;
54
+
55
+ // roc statistics
56
+ state.rocMean = 0;
57
+ state.rocVariance = 0;
58
+
59
+ // Acceleration hint configuration
60
+ if ( state.stats.accelerationHint ) {
61
+ state.rocSmoothedFast = 0;
62
+ state.accelAlpha = halfLifeToAlpha( state.rocStatsHalfLife / state.speedUp );
63
+ state.accelerationHint = null;
64
+ }
65
+
66
+ // Tracking
67
+ state.samples = 0;
68
+ state.consistentSamples = 0;
69
+ state.trend = 'learning';
70
+ state.previousTrend = 'learning';
71
+ state.confidence = 0;
72
+ state.snr = 0;
73
+ // accelThreshold is derived from rocThreshold at runtime (rocThreshold may be dynamic)
74
+
75
+ // Node metadata
76
+ state.nodeType = getNodeType();
77
+
78
+ return state;
79
+ }; // init()
80
+
81
+ export default init;
@@ -0,0 +1,151 @@
1
+ // nodes/trend/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
+ 'trend',
8
+ 'confidence',
9
+ 'rocMean',
10
+ 'accelerationHint'
11
+ ];
12
+
13
+ /** Human-readable descriptions (for introspection) */
14
+ const STAT_DESCRIPTIONS = {
15
+ trend: 'Trend direction: learning, stable, rising, or falling',
16
+ confidence: 'Confidence score between 0 and 1',
17
+ rocMean: 'Mean Rate of change (first derivative)',
18
+ accelerationHint: 'Acceleration hint: likely_accelerating, likely_decelerating, or null'
19
+ };
20
+
21
+ /** Control methods available for triggers */
22
+ const SUPPORTED_CONTROL_METHODS = {
23
+ reset: 'Clears accumulated state and restarts computation',
24
+ enable: 'Enables node processing',
25
+ disable: 'Disables node processing',
26
+ pause: 'Pauses node processing while keeping state visible',
27
+ unpause: 'Resumes node processing after pause'
28
+ };
29
+
30
+ /** The type of this node */
31
+ const NODE_TYPE = 'Trend';
32
+
33
+ /** Node capabilities for documentation */
34
+ const CAPABILITIES = {
35
+ description: 'Rate Of Change-domain (first-difference) trend detection with exponentially smoothed roc statistics; no internal smoothing for clean composability.',
36
+ features: [
37
+ 'States: learning, stable, rising, falling',
38
+ 'Confidence: SNR × persistence (tanh mapping)',
39
+ 'Domain knob: rocThreshold (set ≳ noise of the roc signal)',
40
+ 'Optional acceleration hint (fast/slow EWMAs on roc, gated)',
41
+ 'Warm-up derived from roc half-life'
42
+ ]
43
+ };
44
+
45
+ // Export functions for accessing metadata
46
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
47
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
48
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
49
+ export const getNodeType = () => NODE_TYPE;
50
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
51
+
52
+ // Spec default values
53
+ export const DEFAULT_OPTIONS = {
54
+ rocStatsHalfLife: 9,
55
+ rocThreshold: 0.1,
56
+ speedUp: 2
57
+ };
58
+
59
+ /** Parameters that support tunable (dynamic) values */
60
+ export const TUNABLE_PARAMS = [ 'rocThreshold' ];
61
+
62
+ // DSL Metadata for transpilation
63
+ const DSL_METADATA = {
64
+ specSchema: {
65
+ nodeType: {
66
+ type: 'string',
67
+ required: true,
68
+ value: NODE_TYPE
69
+ },
70
+ name: {
71
+ type: 'string',
72
+ required: true,
73
+ validator: validators.identifier,
74
+ error: 'Name must be a valid identifier'
75
+ },
76
+ from: {
77
+ type: 'object',
78
+ required: true,
79
+ properties: {
80
+ x: {
81
+ type: 'string',
82
+ required: true,
83
+ validator: validators.noSpaces,
84
+ error: 'Field name cannot contain spaces'
85
+ }
86
+ }
87
+ },
88
+ stats: {
89
+ type: 'object',
90
+ required: true,
91
+ minProperties: 1,
92
+ propertyNames: SUPPORTED_STATS,
93
+ propertySchema: {
94
+ type: 'object',
95
+ properties: {
96
+ storeAs: {
97
+ type: 'string',
98
+ required: true,
99
+ validator: validators.identifier,
100
+ error: 'storeAs must be a valid identifier'
101
+ }
102
+ }
103
+ }
104
+ },
105
+ rocStatsHalfLife: {
106
+ type: 'numberOrFieldKeyed',
107
+ required: false,
108
+ default: DEFAULT_OPTIONS.rocStatsHalfLife,
109
+ min: 2,
110
+ validator: validators.positive,
111
+ error: 'roc stats half-life must be minimum 2'
112
+ },
113
+ rocThreshold: {
114
+ type: 'numberOrFunctionOrFieldKeyed',
115
+ required: false,
116
+ default: DEFAULT_OPTIONS.rocThreshold,
117
+ // Supports tunable for phase-dependent sensitivity (warmup vs steady-state)
118
+ validator: validators.nonNegativeOrFunction,
119
+ error: 'Threshold must be non-negative or function'
120
+ },
121
+ warmupSamples: {
122
+ type: 'numberOrFieldKeyed',
123
+ required: false,
124
+ integer: true,
125
+ min: 3,
126
+ validator: validators.positiveInteger,
127
+ error: 'Warmup samples must be at least 3'
128
+ },
129
+ speedUp: {
130
+ type: 'numberOrFieldKeyed',
131
+ required: false,
132
+ default: DEFAULT_OPTIONS.speedUp,
133
+ validator: validators.inRange( 1.5, 3 ),
134
+ error: 'speedUp must be between 1.5 and 3'
135
+ }
136
+ },
137
+
138
+ crossFieldValidators: [],
139
+
140
+ // Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
141
+ buildSpec: ( name, x, stats, options ) => ( {
142
+ nodeType: NODE_TYPE,
143
+ name,
144
+ from: { x },
145
+ stats,
146
+ ...options
147
+ } ),
148
+
149
+ };
150
+
151
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,22 @@
1
+ // nodes/trend/publish-to.js
2
+
3
+ import { publishNaN } from '../../core/utils/node/index.js';
4
+
5
+ const publishTo = function ( state, msg ) {
6
+ if ( state.disable ) return;
7
+ // Propagate NaN if unhealthy
8
+ if ( state.inputValidationFailed ) {
9
+ publishNaN( state, msg );
10
+ return;
11
+ }
12
+
13
+ // Iterate only over configured stats
14
+ const stats = state.stats;
15
+ for ( const statName in stats ) {
16
+ if ( Object.prototype.hasOwnProperty.call(stats, statName ) ) {
17
+ msg[ stats[ statName ].storeAs ] = state[ statName ];
18
+ }
19
+ }
20
+ }; // publishTo()
21
+
22
+ export default publishTo;