@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,155 @@
1
+ // nodes/ratio/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
+ 'ratio'
8
+ ];
9
+
10
+ /** Human-readable descriptions (for introspection) */
11
+ const STAT_DESCRIPTIONS = {
12
+ ratio: 'Ratio of x to y (x/y), optionally scaled or in decibels'
13
+ };
14
+
15
+ /** Control methods available for triggers */
16
+ const SUPPORTED_CONTROL_METHODS = {
17
+ reset: 'Clears accumulated state and restarts computation',
18
+ enable: 'Enables node processing',
19
+ disable: 'Disables node processing',
20
+ pause: 'Pauses node processing while keeping state visible',
21
+ unpause: 'Resumes node processing after pause'
22
+ };
23
+
24
+ /** The type of this node */
25
+ const NODE_TYPE = 'Ratio';
26
+
27
+ /** Node capabilities for documentation */
28
+ const CAPABILITIES = {
29
+ description: 'Computes the ratio of two numeric fields with optional logarithmic scale',
30
+ features: [
31
+ 'Linear ratio computation (x/y)',
32
+ 'Linear scaling of ratio output (x/y × k) for unit conversion',
33
+ 'Decibel scale output (20*log₁₀(x/y)) for SNR and gain measurements',
34
+ 'Numerically stable logarithm implementation',
35
+ 'Division-by-zero protection with configurable minimum threshold',
36
+ 'NaN propagation for invalid inputs'
37
+ ]
38
+ };
39
+
40
+ // Attach static metadata once, from our constants by making a shallow copy.
41
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
42
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
43
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
44
+ export const getNodeType = () => NODE_TYPE;
45
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
46
+
47
+ export const DEFAULT_OPTIONS = {
48
+ logScale: false,
49
+ minY: 1e-10,
50
+ scaleBy: 1
51
+ };
52
+
53
+ // DSL Metadata for transpilation
54
+ const DSL_METADATA = {
55
+ // Spec schema for validation
56
+ specSchema: {
57
+ nodeType: {
58
+ type: 'string',
59
+ required: true,
60
+ value: NODE_TYPE
61
+ },
62
+ name: {
63
+ type: 'string',
64
+ required: true,
65
+ validator: validators.identifier,
66
+ error: 'Name must be a valid identifier'
67
+ },
68
+ from: {
69
+ type: 'object',
70
+ required: true,
71
+ properties: {
72
+ x: {
73
+ type: 'string',
74
+ required: true,
75
+ validator: validators.noSpaces,
76
+ error: 'Field name cannot contain spaces'
77
+ },
78
+ y: {
79
+ type: 'string',
80
+ required: true,
81
+ validator: validators.noSpaces,
82
+ error: 'Field name cannot contain spaces'
83
+ }
84
+ }
85
+ },
86
+ logScale: {
87
+ type: 'boolean',
88
+ required: false,
89
+ default: false
90
+ },
91
+ minY: {
92
+ type: 'number',
93
+ required: false,
94
+ default: 1e-10, // Prevents division by extremely small numbers
95
+ validator: (value) => value >= 0,
96
+ error: 'minY must be non-negative'
97
+ },
98
+ scaleBy: {
99
+ type: 'number',
100
+ required: false,
101
+ default: 1,
102
+ validator: ( value ) => value > 0,
103
+ error: 'scaleBy must be a positive number'
104
+ },
105
+ stats: {
106
+ type: 'object',
107
+ required: true,
108
+ propertyNames: SUPPORTED_STATS,
109
+ propertySchema: {
110
+ type: 'object',
111
+ required: true,
112
+ properties: {
113
+ storeAs: {
114
+ type: 'string',
115
+ required: true,
116
+ minLength: 1,
117
+ validator: validators.identifier,
118
+ error: 'storeAs must be a valid identifier'
119
+ }
120
+ }
121
+ }
122
+ }
123
+ },
124
+
125
+ // Cross-field validators
126
+ crossFieldValidators: [
127
+ {
128
+ fields: [ 'from.x', 'from.y' ],
129
+ validator: ( spec ) => spec.from.x !== spec.from.y,
130
+ error: 'from.x and from.y must be different fields'
131
+ },
132
+ {
133
+ fields: [ 'logScale', 'scaleBy' ],
134
+ validator: ( spec ) => {
135
+ const hasLogScale = spec.logScale === true;
136
+ const hasScaleBy = spec.scaleBy !== undefined;
137
+ return !( hasLogScale && hasScaleBy );
138
+ },
139
+ error: 'logScale and scaleBy are mutually exclusive'
140
+ }
141
+ ],
142
+
143
+ // How to build the spec from DSL params
144
+ // Pattern: NAME_X_Y_OUTPUTS_OPTIONS -> (name, x, y, stats, options)
145
+ buildSpec: ( name, x, y, stats, options ) => ( {
146
+ nodeType: NODE_TYPE,
147
+ name,
148
+ from: { x, y },
149
+ stats,
150
+ ...options
151
+ } ),
152
+
153
+ };
154
+
155
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,15 @@
1
+ // nodes/ratio/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
+ msg[ state.stats.ratio.storeAs ] = state.ratio;
13
+ }; // publishTo()
14
+
15
+ export default publishTo;
@@ -0,0 +1,7 @@
1
+ // nodes/ratio/recompute.js
2
+
3
+ const recompute = function () {
4
+ return true;
5
+ }; // recompute()
6
+
7
+ export default recompute;
@@ -0,0 +1,7 @@
1
+ // nodes/ratio/reset.js
2
+
3
+ const reset = function () {
4
+ return true;
5
+ }; // reset()
6
+
7
+ export default reset;
@@ -0,0 +1,47 @@
1
+ // nodes/ratio/update.js
2
+
3
+ const LN_TO_DB = 20 / Math.LN10; // ≈ 8.6858896380650365
4
+
5
+ const update = function ( state, msg ) {
6
+ if ( state.disable || state.pause ) return state;
7
+ const xVal = msg[ state.x ];
8
+ // Reset on each update
9
+ state.inputValidationFailed = false;
10
+ // Handle faults gracefully: ensure their isolation
11
+ if ( !Number.isFinite( xVal ) ) {
12
+ // Signals publishing NaN for all demanded `stats` in publishTo.
13
+ state.inputValidationFailed = true;
14
+ return state;
15
+ }
16
+
17
+ const yVal = msg[ state.y ];
18
+ // Handle faults gracefully: ensure their isolation
19
+ if ( !Number.isFinite( yVal ) ) {
20
+ // Signals publishing NaN for all demanded `stats` in publishTo.
21
+ state.inputValidationFailed = true;
22
+ return state;
23
+ }
24
+
25
+ // Protection against division by zero or very small numbers.
26
+ // Returns NaN to signal invalid computation - downstream nodes
27
+ // can detect and handle this appropriately.
28
+ if ( Math.abs( yVal ) < state.minY ) {
29
+ state.ratio = NaN; // Signal invalid ratio
30
+ return state;
31
+ }
32
+
33
+ // For log scale, both values must be positive.
34
+ // Log of non-positive numbers is undefined.
35
+ if ( state.logScale && ( xVal <= 0 || yVal <= 0 ) ) {
36
+ state.ratio = NaN; // log of non-positive is undefined
37
+ return state;
38
+ }
39
+
40
+ state.ratio = ( state.logScale ) ?
41
+ ( Math.log( xVal ) - Math.log( yVal ) ) * LN_TO_DB :
42
+ ( xVal / yVal ) * state.scaleBy;
43
+
44
+ return state;
45
+ }; // update()
46
+
47
+ export default update;
@@ -0,0 +1,82 @@
1
+ /**
2
+ * @fileoverview Sanitize Node - Universal data validation gate
3
+ *
4
+ * Validates data through configurable checks and maps invalid values to NaN.
5
+ * This provides a universal invalid marker that downstream nodes can detect
6
+ * with the fast check: value !== value
7
+ *
8
+ * Features:
9
+ * - Range validation with per-parameter bounds
10
+ * - Value list validation with allow/deny modes
11
+ * - Custom predicate validation
12
+ * - Works with both numeric and categorical data
13
+ *
14
+ * Configuration:
15
+ * - ranges: Object mapping field names to {min, max} bounds
16
+ * - valueList: Array of values to check against
17
+ * - containsValidValues: false = deny list (default), true = allow list
18
+ * - predicate: Optional function(value, msg) returning boolean
19
+ *
20
+ * Usage Examples:
21
+ *
22
+ * // Range validation (single field)
23
+ * .sanitize('temperature', {
24
+ * ranges: {
25
+ * temperature: { min: -40, max: 150 }
26
+ * }
27
+ * })
28
+ *
29
+ * // Multi-field with apply pattern
30
+ * .sanitize('sensors', ['temperature', 'pressure', 'flow'], ['sanitized'], {
31
+ * ranges: {
32
+ * temperature: { min: -40, max: 150 },
33
+ * pressure: { min: 0, max: 200 },
34
+ * flow: { min: 0, max: 1000 }
35
+ * }
36
+ * })
37
+ *
38
+ * // Deny list (blacklist) - common error codes
39
+ * .sanitize('signal', {
40
+ * valueList: [32767, -9999, "ERROR", "N/A"],
41
+ * containsValidValues: false
42
+ * })
43
+ *
44
+ * // Allow list (whitelist) - valid states only
45
+ * .sanitize('state', {
46
+ * valueList: ["RUNNING", "IDLE", "STOPPED"],
47
+ * containsValidValues: true
48
+ * })
49
+ *
50
+ * // Custom validation with predicate
51
+ * .sanitize('reading', {
52
+ * predicate: (value, msg) => value > msg.ambient
53
+ * })
54
+ *
55
+ * // Combined validation (checks in order: range, valueList, predicate)
56
+ * .sanitize('sensor', {
57
+ * ranges: {
58
+ * sensor: { min: 0, max: 100 }
59
+ * },
60
+ * valueList: [32767, -9999],
61
+ * containsValidValues: false,
62
+ * predicate: (value, msg) => value !== msg.lastValue
63
+ * })
64
+ *
65
+ * Failure Reporting:
66
+ * - failureReason: 'range' | 'valueList' | 'predicate' | null
67
+ * - failedValue: The value that failed validation
68
+ *
69
+ */
70
+
71
+ export { default as init } from './init.js';
72
+ export { default as update } from './update.js';
73
+ export { default as reset } from './reset.js';
74
+ export { default as recompute } from './recompute.js';
75
+ export { default as publishTo } from './publish-to.js';
76
+ // Direct imports for common control functions
77
+ export { default as disable } from '../../core/utils/node/disable.js';
78
+ export { default as enable } from '../../core/utils/node/enable.js';
79
+ export { default as pause } from '../../core/utils/node/pause.js';
80
+ export { default as unpause } from '../../core/utils/node/unpause.js';
81
+ // Re-export everything from introspection.js
82
+ export * from './introspect.js';
@@ -0,0 +1,82 @@
1
+ import { validateWithSchema } from '../../core/utils/validate/index.js';
2
+ import { getDSLMetadata, getNodeType, DEFAULT_OPTIONS } from './introspect.js';
3
+ import { resolveNestedObject, resolveArray } from '../../core/utils/options/resolve-field-keyed.js';
4
+ import { asTunable } from '../../core/tunable/index.js';
5
+
6
+ const init = function ( spec ) {
7
+ // Validate specification
8
+ const metadata = getDSLMetadata();
9
+ const validation = validateWithSchema(
10
+ {
11
+ ...metadata.specSchema,
12
+ _crossFieldValidators: metadata.crossFieldValidators
13
+ },
14
+ spec,
15
+ 'spec'
16
+ );
17
+ validation.throwIfInvalid( getNodeType() );
18
+
19
+ // Create state after validation passes
20
+ const state = Object.create( null );
21
+
22
+ // Ensure node is enabled by default
23
+ state.disable = false;
24
+ state.pause = false;
25
+
26
+ // Core configuration
27
+ state.x = spec.from.x;
28
+ state.stats = spec.stats;
29
+
30
+ // Pre-allocate failure reason strings (no allocations in hot path)
31
+ state.REASON_RANGE = 'range';
32
+ state.REASON_VALUE_LIST = 'valueList';
33
+ state.REASON_PREDICATE = 'predicate';
34
+
35
+ // Range configuration. Three shapes, all resolved by resolveNestedObject:
36
+ // Direct: { ranges: { min: -40, max: 85 } }
37
+ // Field-keyed: { ranges: { temp: { min: -40, max: 85 } } }
38
+ // Tunable: { ranges: lookupByField('shift', { day: {...}, night: {...} }, {...}) }
39
+ if ( typeof spec.ranges === 'function' ) {
40
+ // Dynamic ranges (tunable) - resolve at runtime
41
+ state.rangesFn = spec.ranges;
42
+ state.resolvedRangeSpec = null; // Not yet resolved
43
+ state.hasRange = true;
44
+ } else {
45
+ // Static ranges - resolve field-keying at init time
46
+ const rangeSpec = resolveNestedObject( spec.ranges, state.x, [ 'min', 'max' ] );
47
+ if ( rangeSpec ) {
48
+ state.rangesFn = asTunable( rangeSpec );
49
+ state.resolvedRangeSpec = rangeSpec; // Known at init
50
+ state.hasRange = true;
51
+ } else {
52
+ state.hasRange = false;
53
+ state.rangesFn = null;
54
+ state.resolvedRangeSpec = null;
55
+ }
56
+ }
57
+ state.tunableErrorLogged = false;
58
+
59
+ // Value list configuration (use Set for O(1) lookups)
60
+ // Supports both direct: { valueList: [1, 2, 3] }
61
+ // and field-keyed: { valueList: { temp: [1, 2], pressure: [3, 4] } }
62
+ const valueListSpec = resolveArray( spec.valueList, state.x );
63
+ const hasValueList = valueListSpec?.length > 0;
64
+ state.valueSet = hasValueList ? new Set( valueListSpec ) : null;
65
+ state.containsValidValues = spec.containsValidValues ?? DEFAULT_OPTIONS.containsValidValues;
66
+
67
+ // Custom predicate
68
+ state.predicate = spec.predicate || null;
69
+ // Log suppression: one log per error episode; reset on recovery.
70
+ state.predicateErrorLogged = false;
71
+
72
+ // Node metadata
73
+ state.nodeType = getNodeType();
74
+
75
+ // Initialize stats (null = no failure)
76
+ state.failureReason = null;
77
+ state.failedValue = null;
78
+
79
+ return state;
80
+ }; // init()
81
+
82
+ export default init;
@@ -0,0 +1,194 @@
1
+ import { validators } from '../../core/utils/validate/index.js';
2
+ import { resolveNestedObject, resolveArray } from '../../core/utils/options/resolve-field-keyed.js';
3
+
4
+ /** Single source of truth for what this plugin can do */
5
+ const SUPPORTED_STATS = [
6
+ 'failureReason',
7
+ 'failedValue'
8
+ ];
9
+
10
+ /** Human-readable descriptions (for introspection) */
11
+ const STAT_DESCRIPTIONS = {
12
+ failureReason: 'Type of validation failure: "range", "valueList", or "predicate" (null if valid)',
13
+ failedValue: 'The value that failed validation (null if valid)'
14
+ };
15
+
16
+ /** Control methods available for triggers */
17
+ const SUPPORTED_CONTROL_METHODS = {
18
+ reset: 'Clears accumulated state and restarts computation',
19
+ enable: 'Enables node processing',
20
+ disable: 'Disables node processing',
21
+ pause: 'Pauses node processing while keeping state visible',
22
+ unpause: 'Resumes node processing after pause'
23
+ };
24
+
25
+ /** The type of this node */
26
+ const NODE_TYPE = 'Sanitize';
27
+
28
+ /** Node capabilities for documentation */
29
+ const CAPABILITIES = {
30
+ description: 'Universal data validation gate mapping invalid values to NaN',
31
+ features: [
32
+ 'Range-based validation with per-parameter bounds',
33
+ 'Value list validation with allow/deny modes',
34
+ 'Custom predicate validation',
35
+ 'Works with numeric and categorical data',
36
+ 'Universal NaN invalid marker for downstream checking'
37
+ ]
38
+ };
39
+
40
+ // Export functions for accessing metadata
41
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
42
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
43
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
44
+ export const getNodeType = () => NODE_TYPE;
45
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
46
+
47
+ // Spec default values
48
+ export const DEFAULT_OPTIONS = {
49
+ valueList: [],
50
+ containsValidValues: false // false = deny list, true = allow list
51
+ };
52
+
53
+ /** Parameters that support tunable (dynamic) values */
54
+ export const TUNABLE_PARAMS = [ 'ranges' ];
55
+
56
+ // DSL Metadata for transpilation
57
+ const DSL_METADATA = {
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
+ minProperties: 1,
86
+ propertyNames: SUPPORTED_STATS,
87
+ propertySchema: {
88
+ type: 'object',
89
+ properties: {
90
+ storeAs: {
91
+ type: 'string',
92
+ required: true,
93
+ validator: validators.identifier,
94
+ error: 'storeAs must be a valid identifier'
95
+ }
96
+ }
97
+ }
98
+ },
99
+ ranges: {
100
+ type: 'nestedObjectOrFunctionOrFieldKeyed',
101
+ required: false,
102
+ // Accepts three shapes the runtime resolver already handles:
103
+ // direct { min: 0, max: 100 }
104
+ // field-keyed { temp: { min: -40, max: 85 }, ... }
105
+ // tunable (msg) => ({ min: msg.lo, max: msg.hi })
106
+ // The inner shape below (both bounds required, numeric) is read by
107
+ // the type validator; min <= max is checked in a cross-field rule.
108
+ properties: {
109
+ min: { type: 'number', required: true },
110
+ max: { type: 'number', required: true }
111
+ }
112
+ },
113
+ valueList: {
114
+ type: 'arrayOrFieldKeyed',
115
+ required: false,
116
+ default: DEFAULT_OPTIONS.valueList,
117
+ itemSchema: {
118
+ type: 'any' // Can be numbers, strings, booleans, etc.
119
+ },
120
+ // eslint-disable-next-line no-self-compare
121
+ validator: ( arr ) => !Array.isArray( arr ) || arr.every( ( x ) => x === x ) // reject NaN
122
+ },
123
+ containsValidValues: {
124
+ type: 'boolean',
125
+ required: false,
126
+ default: DEFAULT_OPTIONS.containsValidValues
127
+ },
128
+ predicate: {
129
+ type: 'function',
130
+ required: false,
131
+ arity: 2,
132
+ error: 'Predicate must accept (value, msg) parameters'
133
+ }
134
+ },
135
+
136
+ crossFieldValidators: [
137
+ {
138
+ fields: [ 'ranges', 'valueList', 'predicate' ],
139
+ validator: ( spec ) => {
140
+ // ranges can be a function (tunable) or object (direct or field-keyed)
141
+ const hasRanges = typeof spec.ranges === 'function' ||
142
+ ( spec.ranges && Object.keys( spec.ranges ).length > 0 );
143
+ // Resolve valueList for the node's field so a direct array and a
144
+ // field-keyed map both count as "a method was provided".
145
+ const valueList = resolveArray( spec.valueList, spec.from?.x );
146
+ const hasValueList = Array.isArray( valueList ) && valueList.length > 0;
147
+ const hasPredicate = typeof spec.predicate === 'function';
148
+ return hasRanges || hasValueList || hasPredicate;
149
+ },
150
+ error: 'Must provide at least one validation method: ranges, valueList, or predicate'
151
+ },
152
+ {
153
+ fields: [ 'from.x', 'ranges' ],
154
+ validator: ( spec ) => {
155
+ // If ranges is a function, validation deferred to runtime
156
+ if ( typeof spec.ranges === 'function' ) return true;
157
+ // No ranges specified — nothing to check here
158
+ if ( !spec.ranges || Object.keys( spec.ranges ).length === 0 ) {
159
+ return true;
160
+ }
161
+ // A direct range resolves for this field; a field-keyed map must
162
+ // carry an entry for it. Use the same resolver the runtime uses
163
+ // so validation and runtime agree on what "resolves" means.
164
+ return resolveNestedObject( spec.ranges, spec.from.x, [ 'min', 'max' ] ) !== undefined;
165
+ },
166
+ error: 'Ranges must be a direct { min, max } or a per-field map that includes the field this node reads'
167
+ },
168
+ {
169
+ fields: [ 'ranges' ],
170
+ validator: ( spec ) => {
171
+ // If ranges is a function, validation deferred to runtime
172
+ if ( typeof spec.ranges === 'function' ) return true;
173
+ if ( !spec.ranges ) return true;
174
+ // A direct range carries min/max itself; a field-keyed map carries
175
+ // one range per field. Check min <= max on whichever shape was given.
176
+ const isDirect = ( 'min' in spec.ranges ) || ( 'max' in spec.ranges );
177
+ const ranges = isDirect ? [ spec.ranges ] : Object.values( spec.ranges );
178
+ return ranges.every( ( range ) => range.min <= range.max );
179
+ },
180
+ error: 'Range min must be less than or equal to max'
181
+ }
182
+ ],
183
+
184
+ buildSpec: ( name, x, stats, options ) => ( {
185
+ nodeType: NODE_TYPE,
186
+ name,
187
+ from: { x },
188
+ stats,
189
+ ...options
190
+ } ),
191
+
192
+ };
193
+
194
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,15 @@
1
+ const publishTo = function ( state, msg ) {
2
+ if ( state.disable ) return;
3
+
4
+ // Publish failure information if present
5
+ if ( state.failureReason ) {
6
+ if ( state.stats.failureReason ) {
7
+ msg[ state.stats.failureReason.storeAs ] = state.failureReason;
8
+ }
9
+ if ( state.stats.failedValue ) {
10
+ msg[ state.stats.failedValue.storeAs ] = state.failedValue;
11
+ }
12
+ }
13
+ }; // publishTo()
14
+
15
+ export default publishTo;
@@ -0,0 +1,8 @@
1
+ const recompute = function ( _state ) {
2
+ // No long-running accumulations
3
+ // Validation is stateless per sample
4
+
5
+ return true;
6
+ }; // recompute()
7
+
8
+ export default recompute;
@@ -0,0 +1,14 @@
1
+ const reset = function ( state ) {
2
+ // Stateless validation node - nothing to reset
3
+ // Failure status is per-message, not accumulated
4
+
5
+ state.failureReason = null;
6
+ state.failedValue = null;
7
+ // Clear error suppression so next error episode is logged
8
+ state.predicateErrorLogged = false;
9
+ state.tunableErrorLogged = false;
10
+
11
+ return state;
12
+ }; // reset()
13
+
14
+ export default reset;