@winkjs/composer 0.0.2 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +42 -103
  3. package/package.json +53 -29
  4. package/src/composer.js +75 -0
  5. package/src/core/batch/aoo-encoder.js +148 -0
  6. package/src/core/batch/index.js +12 -0
  7. package/src/core/batch/sliding-window.js +91 -0
  8. package/src/core/codec/index.js +137 -0
  9. package/src/core/emitter-manager/mqtt/constants.js +102 -0
  10. package/src/core/emitter-manager/mqtt/emitter.js +780 -0
  11. package/src/core/emitter-manager/mqtt/index.js +190 -0
  12. package/src/core/emitter-manager/mqtt/mqtt-store.js +538 -0
  13. package/src/core/emitter-manager/terminal/assert-columns.js +98 -0
  14. package/src/core/emitter-manager/terminal/formatters.js +160 -0
  15. package/src/core/emitter-manager/terminal/index.js +341 -0
  16. package/src/core/env-vars.js +245 -0
  17. package/src/core/mqtt-protocol.js +47 -0
  18. package/src/core/partition-manager/index.js +7 -0
  19. package/src/core/partition-manager/init.js +44 -0
  20. package/src/core/partition-manager/introspect.js +15 -0
  21. package/src/core/partition-manager/publish-to.js +6 -0
  22. package/src/core/partition-manager/recompute.js +5 -0
  23. package/src/core/partition-manager/reset.js +5 -0
  24. package/src/core/partition-manager/update.js +264 -0
  25. package/src/core/partition-manager/validate-flow.js +157 -0
  26. package/src/core/semantics/digest.js +128 -0
  27. package/src/core/semantics/index.js +83 -0
  28. package/src/core/semantics/loader.js +571 -0
  29. package/src/core/semantics/schemas/asset-class-schema.js +218 -0
  30. package/src/core/semantics/schemas/column-schema.js +651 -0
  31. package/src/core/semantics/schemas/enum-schema.js +153 -0
  32. package/src/core/semantics/schemas/index.js +51 -0
  33. package/src/core/semantics/warnings.js +141 -0
  34. package/src/core/shutdown-manager/index.js +289 -0
  35. package/src/core/source-manager/csv/index.js +171 -0
  36. package/src/core/source-manager/csv/start.js +456 -0
  37. package/src/core/source-manager/mqtt/client.js +406 -0
  38. package/src/core/source-manager/mqtt/constants.js +147 -0
  39. package/src/core/source-manager/mqtt/dedup.js +244 -0
  40. package/src/core/source-manager/mqtt/index.js +260 -0
  41. package/src/core/source-manager/mqtt/status.js +546 -0
  42. package/src/core/source-manager/test-harness/comparator.js +226 -0
  43. package/src/core/source-manager/test-harness/field-generator.js +85 -0
  44. package/src/core/source-manager/test-harness/fuzz.js +87 -0
  45. package/src/core/source-manager/test-harness/index.js +115 -0
  46. package/src/core/source-manager/test-harness/prng.js +57 -0
  47. package/src/core/source-manager/test-harness/start.js +232 -0
  48. package/src/core/source-manager/test-harness/validate.js +213 -0
  49. package/src/core/storage-manager/questdb/assert-columns.js +120 -0
  50. package/src/core/storage-manager/questdb/ensure-tables.js +187 -0
  51. package/src/core/storage-manager/questdb/index.js +1251 -0
  52. package/src/core/storage-manager/questdb/persist-plan.js +488 -0
  53. package/src/core/storage-manager/questdb/writers.js +142 -0
  54. package/src/core/test-utils/sealed-record.js +80 -0
  55. package/src/core/test-utils/soak-signature.js +149 -0
  56. package/src/core/test-utils/tcp-proxy.js +106 -0
  57. package/src/core/tunable/helpers.js +254 -0
  58. package/src/core/tunable/index.js +87 -0
  59. package/src/core/utils/console-formatter/index.js +207 -0
  60. package/src/core/utils/create-smart-store-as/index.js +98 -0
  61. package/src/core/utils/create-smart-store-as/sanitize-field.js +46 -0
  62. package/src/core/utils/create-smart-store-as/smart-truncate.js +93 -0
  63. package/src/core/utils/flow/index.js +5 -0
  64. package/src/core/utils/flow/make-collision-checker.js +23 -0
  65. package/src/core/utils/flow/to-kebab.js +20 -0
  66. package/src/core/utils/half-life/index.js +111 -0
  67. package/src/core/utils/node/disable.js +11 -0
  68. package/src/core/utils/node/enable.js +11 -0
  69. package/src/core/utils/node/execute-triggers.js +40 -0
  70. package/src/core/utils/node/index.js +10 -0
  71. package/src/core/utils/node/pause.js +11 -0
  72. package/src/core/utils/node/populate-predicate-inputs.js +14 -0
  73. package/src/core/utils/node/publish-nan.js +19 -0
  74. package/src/core/utils/node/reset-predicate-inputs.js +11 -0
  75. package/src/core/utils/node/unpause.js +11 -0
  76. package/src/core/utils/node/validate-spec.js +22 -0
  77. package/src/core/utils/options/resolve-field-keyed.js +115 -0
  78. package/src/core/utils/quantize/index.js +75 -0
  79. package/src/core/utils/stats/formulas.js +67 -0
  80. package/src/core/utils/stats/index.js +3 -0
  81. package/src/core/utils/validate/composer-validators.js +141 -0
  82. package/src/core/utils/validate/helpers.js +206 -0
  83. package/src/core/utils/validate/index.js +12 -0
  84. package/src/core/utils/validate/type-validators.js +538 -0
  85. package/src/core/utils/validate/validate.js +107 -0
  86. package/src/core/utils/validate/validators.js +177 -0
  87. package/src/core/wiring/assert-handle.js +57 -0
  88. package/src/core/wiring/assert-module.js +63 -0
  89. package/src/core/wiring/index.js +5 -0
  90. package/src/core/wiring/node-type-to-module.js +17 -0
  91. package/src/core/wiring/wire-emitters.js +256 -0
  92. package/src/core/wiring/wire-linear-graph.js +25 -0
  93. package/src/core/wiring/wire-node.js +157 -0
  94. package/src/core/wiring/wire-semantics.js +173 -0
  95. package/src/core/wiring/wire-storages.js +344 -0
  96. package/src/flow/check-output-collisions.js +93 -0
  97. package/src/flow/consts.js +9 -0
  98. package/src/flow/deep-clone-spec.js +64 -0
  99. package/src/flow/driver.js +171 -0
  100. package/src/flow/expand-single-input-array.js +76 -0
  101. package/src/flow/expand-template-spec.js +93 -0
  102. package/src/flow/flow-api-schemas.js +300 -0
  103. package/src/flow/flow.js +658 -0
  104. package/src/flow/for-each.js +225 -0
  105. package/src/flow/get-signature-pattern.js +31 -0
  106. package/src/flow/get-target-array.js +46 -0
  107. package/src/flow/inspect.js +67 -0
  108. package/src/flow/load-node-module.js +81 -0
  109. package/src/flow/make-node-method.js +112 -0
  110. package/src/flow/resolve-pick-by-field.js +77 -0
  111. package/src/flow/resolve-tunables.js +76 -0
  112. package/src/flow/run.js +392 -0
  113. package/src/flow/serialize.js +161 -0
  114. package/src/flow/signatures-schema.js +222 -0
  115. package/src/flow/spec-builder.js +65 -0
  116. package/src/flow/validate-triggers.js +250 -0
  117. package/src/flow/validate.js +261 -0
  118. package/src/nodes/accumulate/index.js +21 -0
  119. package/src/nodes/accumulate/init.js +48 -0
  120. package/src/nodes/accumulate/introspect.js +123 -0
  121. package/src/nodes/accumulate/publish-to.js +34 -0
  122. package/src/nodes/accumulate/recompute.js +27 -0
  123. package/src/nodes/accumulate/reset.js +25 -0
  124. package/src/nodes/accumulate/update.js +42 -0
  125. package/src/nodes/appraise/calibrate.js +64 -0
  126. package/src/nodes/appraise/decision.js +66 -0
  127. package/src/nodes/appraise/deviation.js +140 -0
  128. package/src/nodes/appraise/index.js +23 -0
  129. package/src/nodes/appraise/init.js +165 -0
  130. package/src/nodes/appraise/integrate.js +45 -0
  131. package/src/nodes/appraise/introspect.js +352 -0
  132. package/src/nodes/appraise/publish-to.js +93 -0
  133. package/src/nodes/appraise/receptor.js +69 -0
  134. package/src/nodes/appraise/recompute.js +79 -0
  135. package/src/nodes/appraise/reset.js +53 -0
  136. package/src/nodes/appraise/update.js +130 -0
  137. package/src/nodes/archive/csv/index.js +6 -0
  138. package/src/nodes/archive/csv/init.js +36 -0
  139. package/src/nodes/archive/csv/load.js +70 -0
  140. package/src/nodes/archive/csv/reset.js +14 -0
  141. package/src/nodes/archive/csv/simple-parser.js +216 -0
  142. package/src/nodes/archive/csv/test.js +173 -0
  143. package/src/nodes/archive/csv/update.js +63 -0
  144. package/src/nodes/butterworth-filter/index.js +18 -0
  145. package/src/nodes/butterworth-filter/init.js +184 -0
  146. package/src/nodes/butterworth-filter/introspect.js +228 -0
  147. package/src/nodes/butterworth-filter/publish-to.js +21 -0
  148. package/src/nodes/butterworth-filter/recompute.js +13 -0
  149. package/src/nodes/butterworth-filter/reset.js +30 -0
  150. package/src/nodes/butterworth-filter/update.js +38 -0
  151. package/src/nodes/categorize/index.js +19 -0
  152. package/src/nodes/categorize/init.js +58 -0
  153. package/src/nodes/categorize/introspect.js +177 -0
  154. package/src/nodes/categorize/publish-to.js +29 -0
  155. package/src/nodes/categorize/recompute.js +14 -0
  156. package/src/nodes/categorize/reset.js +16 -0
  157. package/src/nodes/categorize/update.js +53 -0
  158. package/src/nodes/controller/index.js +9 -0
  159. package/src/nodes/controller/init.js +55 -0
  160. package/src/nodes/controller/introspect.js +91 -0
  161. package/src/nodes/controller/publish-to.js +8 -0
  162. package/src/nodes/controller/recompute.js +9 -0
  163. package/src/nodes/controller/reset.js +17 -0
  164. package/src/nodes/controller/update.js +63 -0
  165. package/src/nodes/diff/index.js +19 -0
  166. package/src/nodes/diff/init.js +37 -0
  167. package/src/nodes/diff/introspect.js +132 -0
  168. package/src/nodes/diff/publish-to.js +21 -0
  169. package/src/nodes/diff/recompute.js +12 -0
  170. package/src/nodes/diff/reset.js +12 -0
  171. package/src/nodes/diff/update.js +37 -0
  172. package/src/nodes/digest-moments/formulas.js +12 -0
  173. package/src/nodes/digest-moments/index.js +14 -0
  174. package/src/nodes/digest-moments/init.js +67 -0
  175. package/src/nodes/digest-moments/introspect.js +154 -0
  176. package/src/nodes/digest-moments/publish-to.js +34 -0
  177. package/src/nodes/digest-moments/recompute.js +14 -0
  178. package/src/nodes/digest-moments/reset.js +14 -0
  179. package/src/nodes/digest-moments/update.js +120 -0
  180. package/src/nodes/dwell-time-tracker/index.js +14 -0
  181. package/src/nodes/dwell-time-tracker/init.js +59 -0
  182. package/src/nodes/dwell-time-tracker/introspect.js +137 -0
  183. package/src/nodes/dwell-time-tracker/publish-to.js +22 -0
  184. package/src/nodes/dwell-time-tracker/recompute.js +8 -0
  185. package/src/nodes/dwell-time-tracker/reset.js +23 -0
  186. package/src/nodes/dwell-time-tracker/update.js +103 -0
  187. package/src/nodes/emit-if/helpers.js +94 -0
  188. package/src/nodes/emit-if/index.js +16 -0
  189. package/src/nodes/emit-if/init.js +67 -0
  190. package/src/nodes/emit-if/introspect.js +102 -0
  191. package/src/nodes/emit-if/publish-to.js +13 -0
  192. package/src/nodes/emit-if/recompute.js +12 -0
  193. package/src/nodes/emit-if/reset.js +27 -0
  194. package/src/nodes/emit-if/update.js +93 -0
  195. package/src/nodes/es-correlation/index.js +19 -0
  196. package/src/nodes/es-correlation/init.js +73 -0
  197. package/src/nodes/es-correlation/introspect.js +176 -0
  198. package/src/nodes/es-correlation/publish-to.js +32 -0
  199. package/src/nodes/es-correlation/recompute.js +56 -0
  200. package/src/nodes/es-correlation/reset.js +27 -0
  201. package/src/nodes/es-correlation/update.js +110 -0
  202. package/src/nodes/es-mean/index.js +20 -0
  203. package/src/nodes/es-mean/init.js +58 -0
  204. package/src/nodes/es-mean/introspect.js +129 -0
  205. package/src/nodes/es-mean/publish-to.js +19 -0
  206. package/src/nodes/es-mean/recompute.js +16 -0
  207. package/src/nodes/es-mean/reset.js +24 -0
  208. package/src/nodes/es-mean/update.js +69 -0
  209. package/src/nodes/es-pairwise-correlation/index.js +19 -0
  210. package/src/nodes/es-pairwise-correlation/init.js +98 -0
  211. package/src/nodes/es-pairwise-correlation/introspect.js +187 -0
  212. package/src/nodes/es-pairwise-correlation/publish-to.js +31 -0
  213. package/src/nodes/es-pairwise-correlation/recompute.js +69 -0
  214. package/src/nodes/es-pairwise-correlation/reset.js +29 -0
  215. package/src/nodes/es-pairwise-correlation/update.js +89 -0
  216. package/src/nodes/es-stats/index.js +21 -0
  217. package/src/nodes/es-stats/init.js +105 -0
  218. package/src/nodes/es-stats/introspect.js +157 -0
  219. package/src/nodes/es-stats/publish-to.js +42 -0
  220. package/src/nodes/es-stats/recompute.js +74 -0
  221. package/src/nodes/es-stats/reset.js +49 -0
  222. package/src/nodes/es-stats/update-envelope.js +44 -0
  223. package/src/nodes/es-stats/update-welford.js +64 -0
  224. package/src/nodes/es-stats/update.js +59 -0
  225. package/src/nodes/index.js +83 -0
  226. package/src/nodes/invert-flag/index.js +20 -0
  227. package/src/nodes/invert-flag/init.js +36 -0
  228. package/src/nodes/invert-flag/introspect.js +115 -0
  229. package/src/nodes/invert-flag/publish-to.js +23 -0
  230. package/src/nodes/invert-flag/recompute.js +12 -0
  231. package/src/nodes/invert-flag/reset.js +12 -0
  232. package/src/nodes/invert-flag/update.js +32 -0
  233. package/src/nodes/kalman1d/index.js +20 -0
  234. package/src/nodes/kalman1d/init.js +103 -0
  235. package/src/nodes/kalman1d/introspect.js +250 -0
  236. package/src/nodes/kalman1d/publish-to.js +34 -0
  237. package/src/nodes/kalman1d/recompute.js +23 -0
  238. package/src/nodes/kalman1d/reset.js +29 -0
  239. package/src/nodes/kalman1d/update.js +144 -0
  240. package/src/nodes/kernel/index.js +18 -0
  241. package/src/nodes/kernel/init.js +66 -0
  242. package/src/nodes/kernel/introspect.js +163 -0
  243. package/src/nodes/kernel/presets.js +74 -0
  244. package/src/nodes/kernel/publish-to.js +25 -0
  245. package/src/nodes/kernel/recompute.js +14 -0
  246. package/src/nodes/kernel/reset.js +20 -0
  247. package/src/nodes/kernel/update.js +69 -0
  248. package/src/nodes/lag/index.js +21 -0
  249. package/src/nodes/lag/init.js +87 -0
  250. package/src/nodes/lag/introspect.js +202 -0
  251. package/src/nodes/lag/publish-to.js +64 -0
  252. package/src/nodes/lag/recompute.js +33 -0
  253. package/src/nodes/lag/reset.js +49 -0
  254. package/src/nodes/lag/update.js +133 -0
  255. package/src/nodes/median3/index.js +19 -0
  256. package/src/nodes/median3/init.js +40 -0
  257. package/src/nodes/median3/introspect.js +116 -0
  258. package/src/nodes/median3/publish-to.js +21 -0
  259. package/src/nodes/median3/recompute.js +12 -0
  260. package/src/nodes/median3/reset.js +15 -0
  261. package/src/nodes/median3/update.js +58 -0
  262. package/src/nodes/moments-digest/copy-from.js +12 -0
  263. package/src/nodes/moments-digest/flush.js +17 -0
  264. package/src/nodes/moments-digest/index.js +132 -0
  265. package/src/nodes/moments-digest/init.js +84 -0
  266. package/src/nodes/moments-digest/introspect.js +122 -0
  267. package/src/nodes/moments-digest/prelude.js +28 -0
  268. package/src/nodes/moments-digest/publish-to.js +31 -0
  269. package/src/nodes/moments-digest/recompute.js +9 -0
  270. package/src/nodes/moments-digest/reset.js +20 -0
  271. package/src/nodes/moments-digest/update.js +182 -0
  272. package/src/nodes/node-names.js +67 -0
  273. package/src/nodes/page-hinkley/index.js +20 -0
  274. package/src/nodes/page-hinkley/init.js +66 -0
  275. package/src/nodes/page-hinkley/introspect.js +193 -0
  276. package/src/nodes/page-hinkley/publish-to.js +28 -0
  277. package/src/nodes/page-hinkley/recompute.js +14 -0
  278. package/src/nodes/page-hinkley/reset.js +23 -0
  279. package/src/nodes/page-hinkley/update.js +105 -0
  280. package/src/nodes/pass-if/index.js +21 -0
  281. package/src/nodes/pass-if/init.js +37 -0
  282. package/src/nodes/pass-if/introspect.js +93 -0
  283. package/src/nodes/pass-if/publish-to.js +15 -0
  284. package/src/nodes/pass-if/recompute.js +15 -0
  285. package/src/nodes/pass-if/reset.js +18 -0
  286. package/src/nodes/pass-if/update.js +37 -0
  287. package/src/nodes/persist-if/helpers.js +111 -0
  288. package/src/nodes/persist-if/index.js +9 -0
  289. package/src/nodes/persist-if/init.js +98 -0
  290. package/src/nodes/persist-if/introspect.js +106 -0
  291. package/src/nodes/persist-if/publish-to.js +21 -0
  292. package/src/nodes/persist-if/recompute.js +20 -0
  293. package/src/nodes/persist-if/reset.js +35 -0
  294. package/src/nodes/persist-if/update.js +112 -0
  295. package/src/nodes/persistence-check/index.js +14 -0
  296. package/src/nodes/persistence-check/init.js +38 -0
  297. package/src/nodes/persistence-check/introspect.js +144 -0
  298. package/src/nodes/persistence-check/publish-to.js +11 -0
  299. package/src/nodes/persistence-check/recompute.js +7 -0
  300. package/src/nodes/persistence-check/reset.js +12 -0
  301. package/src/nodes/persistence-check/update.js +57 -0
  302. package/src/nodes/process-index/index.js +15 -0
  303. package/src/nodes/process-index/init.js +48 -0
  304. package/src/nodes/process-index/introspect.js +182 -0
  305. package/src/nodes/process-index/publish-to.js +29 -0
  306. package/src/nodes/process-index/recompute.js +8 -0
  307. package/src/nodes/process-index/reset.js +8 -0
  308. package/src/nodes/process-index/update.js +75 -0
  309. package/src/nodes/ratio/index.js +14 -0
  310. package/src/nodes/ratio/init.js +37 -0
  311. package/src/nodes/ratio/introspect.js +155 -0
  312. package/src/nodes/ratio/publish-to.js +15 -0
  313. package/src/nodes/ratio/recompute.js +7 -0
  314. package/src/nodes/ratio/reset.js +7 -0
  315. package/src/nodes/ratio/update.js +47 -0
  316. package/src/nodes/sanitize/index.js +82 -0
  317. package/src/nodes/sanitize/init.js +82 -0
  318. package/src/nodes/sanitize/introspect.js +194 -0
  319. package/src/nodes/sanitize/publish-to.js +15 -0
  320. package/src/nodes/sanitize/recompute.js +8 -0
  321. package/src/nodes/sanitize/reset.js +14 -0
  322. package/src/nodes/sanitize/update.js +102 -0
  323. package/src/nodes/spike-guard/index.js +14 -0
  324. package/src/nodes/spike-guard/init.js +47 -0
  325. package/src/nodes/spike-guard/introspect.js +130 -0
  326. package/src/nodes/spike-guard/publish-to.js +35 -0
  327. package/src/nodes/spike-guard/recompute.js +14 -0
  328. package/src/nodes/spike-guard/reset.js +19 -0
  329. package/src/nodes/spike-guard/update.js +89 -0
  330. package/src/nodes/sse-emitter/index.js +7 -0
  331. package/src/nodes/sse-emitter/init.js +105 -0
  332. package/src/nodes/sse-emitter/introspect.js +134 -0
  333. package/src/nodes/sse-emitter/publish-to.js +6 -0
  334. package/src/nodes/sse-emitter/recompute.js +16 -0
  335. package/src/nodes/sse-emitter/reset.js +19 -0
  336. package/src/nodes/sse-emitter/update.js +69 -0
  337. package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +36 -0
  338. package/src/nodes/sse-emitter/utils/build-message.js +31 -0
  339. package/src/nodes/sse-emitter/utils/connection-manager.js +45 -0
  340. package/src/nodes/sse-emitter/utils/endpoint-initializer.js +20 -0
  341. package/src/nodes/sse-emitter/utils/health-monitor.js +38 -0
  342. package/src/nodes/sse-emitter/utils/qos-handler.js +41 -0
  343. package/src/nodes/state-change-detector/helpers.js +35 -0
  344. package/src/nodes/state-change-detector/index.js +12 -0
  345. package/src/nodes/state-change-detector/init.js +56 -0
  346. package/src/nodes/state-change-detector/introspect.js +146 -0
  347. package/src/nodes/state-change-detector/publish-to.js +20 -0
  348. package/src/nodes/state-change-detector/recompute.js +6 -0
  349. package/src/nodes/state-change-detector/reset.js +20 -0
  350. package/src/nodes/state-change-detector/update.js +97 -0
  351. package/src/nodes/sw-stats/index.js +14 -0
  352. package/src/nodes/sw-stats/init.js +51 -0
  353. package/src/nodes/sw-stats/introspect.js +131 -0
  354. package/src/nodes/sw-stats/publish-to.js +45 -0
  355. package/src/nodes/sw-stats/recompute.js +33 -0
  356. package/src/nodes/sw-stats/reset.js +15 -0
  357. package/src/nodes/sw-stats/update.js +39 -0
  358. package/src/nodes/swing-watch/index.js +17 -0
  359. package/src/nodes/swing-watch/init.js +144 -0
  360. package/src/nodes/swing-watch/introspect.js +188 -0
  361. package/src/nodes/swing-watch/publish-to.js +53 -0
  362. package/src/nodes/swing-watch/recompute.js +22 -0
  363. package/src/nodes/swing-watch/reset.js +55 -0
  364. package/src/nodes/swing-watch/update.js +767 -0
  365. package/src/nodes/tally/index.js +15 -0
  366. package/src/nodes/tally/init.js +32 -0
  367. package/src/nodes/tally/introspect.js +116 -0
  368. package/src/nodes/tally/publish-to.js +28 -0
  369. package/src/nodes/tally/recompute.js +10 -0
  370. package/src/nodes/tally/reset.js +10 -0
  371. package/src/nodes/tally/update.js +44 -0
  372. package/src/nodes/threshold/index.js +14 -0
  373. package/src/nodes/threshold/init.js +121 -0
  374. package/src/nodes/threshold/introspect.js +192 -0
  375. package/src/nodes/threshold/publish-to.js +11 -0
  376. package/src/nodes/threshold/recompute.js +8 -0
  377. package/src/nodes/threshold/reset.js +14 -0
  378. package/src/nodes/threshold/update.js +54 -0
  379. package/src/nodes/transform/helpers.js +39 -0
  380. package/src/nodes/transform/index.js +15 -0
  381. package/src/nodes/transform/init.js +35 -0
  382. package/src/nodes/transform/introspect.js +118 -0
  383. package/src/nodes/transform/publish-to.js +23 -0
  384. package/src/nodes/transform/recompute.js +12 -0
  385. package/src/nodes/transform/reset.js +11 -0
  386. package/src/nodes/transform/update.js +32 -0
  387. package/src/nodes/trend/compute-confidence.js +112 -0
  388. package/src/nodes/trend/index.js +14 -0
  389. package/src/nodes/trend/init.js +81 -0
  390. package/src/nodes/trend/introspect.js +151 -0
  391. package/src/nodes/trend/publish-to.js +22 -0
  392. package/src/nodes/trend/recompute.js +15 -0
  393. package/src/nodes/trend/reset.js +26 -0
  394. package/src/nodes/trend/update.js +93 -0
  395. package/src/nodes/tw-stats/copy-from.js +16 -0
  396. package/src/nodes/tw-stats/flush.js +12 -0
  397. package/src/nodes/tw-stats/index.js +41 -0
  398. package/src/nodes/tw-stats/init.js +92 -0
  399. package/src/nodes/tw-stats/introspect.js +171 -0
  400. package/src/nodes/tw-stats/publish-to.js +108 -0
  401. package/src/nodes/tw-stats/recompute.js +9 -0
  402. package/src/nodes/tw-stats/reset.js +16 -0
  403. package/src/nodes/tw-stats/update.js +113 -0
  404. package/src/nodes/unbalance/index.js +15 -0
  405. package/src/nodes/unbalance/init.js +59 -0
  406. package/src/nodes/unbalance/introspect.js +167 -0
  407. package/src/nodes/unbalance/publish-to.js +52 -0
  408. package/src/nodes/unbalance/recompute.js +11 -0
  409. package/src/nodes/unbalance/reset.js +10 -0
  410. package/src/nodes/unbalance/update.js +83 -0
  411. package/src/nodes/vector-distance/index.js +14 -0
  412. package/src/nodes/vector-distance/init.js +54 -0
  413. package/src/nodes/vector-distance/introspect.js +149 -0
  414. package/src/nodes/vector-distance/publish-to.js +35 -0
  415. package/src/nodes/vector-distance/recompute.js +11 -0
  416. package/src/nodes/vector-distance/reset.js +20 -0
  417. package/src/nodes/vector-distance/update.js +139 -0
  418. package/src/nodes/winnow/index.js +12 -0
  419. package/src/nodes/winnow/init.js +82 -0
  420. package/src/nodes/winnow/introspect.js +253 -0
  421. package/src/nodes/winnow/publish-to.js +51 -0
  422. package/src/nodes/winnow/recompute.js +14 -0
  423. package/src/nodes/winnow/reset.js +34 -0
  424. package/src/nodes/winnow/update.js +203 -0
  425. package/src/tools/training/features.js +227 -0
  426. package/src/tools/training/index.js +21 -0
  427. package/src/tools/training/logistic.js +238 -0
  428. package/src/tools/training/metrics.js +126 -0
  429. package/src/tools/training/scale.js +239 -0
  430. package/src/tools/training/validate.js +103 -0
  431. package/src/windowing/count-sliding/create.js +16 -0
  432. package/src/windowing/count-sliding/get-reverse-chronological-index.js +19 -0
  433. package/src/windowing/count-sliding/index.js +6 -0
  434. package/src/windowing/count-sliding/is-not-full.js +5 -0
  435. package/src/windowing/count-sliding/push.js +15 -0
  436. package/src/windowing/count-sliding/reset.js +11 -0
  437. package/src/windowing/count-sliding/size.js +5 -0
  438. package/src/windowing/time-sliding/create.js +25 -0
  439. package/src/windowing/time-sliding/push.js +42 -0
  440. package/CODE_OF_CONDUCT.md +0 -46
  441. package/CONTRIBUTING.md +0 -132
  442. package/docs/.nojekyll +0 -0
  443. package/docs/CONTRIBUTING.md +0 -134
  444. package/docs/README.md +0 -132
  445. package/docs/_coverpage.md +0 -26
  446. package/docs/_media/bg.png +0 -0
  447. package/docs/_media/wink-logo.png +0 -0
  448. package/docs/_navbar.md +0 -14
  449. package/docs/_sidebar.md +0 -2
  450. package/docs/index.html +0 -44
  451. package/eslint.config.js +0 -283
@@ -0,0 +1,59 @@
1
+ // nodes/dwell-time-tracker/init.js
2
+
3
+ import { validateSpec } from '../../core/utils/node/index.js';
4
+ import * as introspect from './introspect.js';
5
+
6
+ const init = function ( spec ) {
7
+ validateSpec( spec, introspect );
8
+
9
+ // Create state after validation passes
10
+ const state = Object.create( null );
11
+ // Standard input parameter health flag during update: false means healthy
12
+ state.inputValidationFailed = false;
13
+ // Ensure node is enabled by default
14
+ state.disable = false;
15
+ state.pause = false;
16
+
17
+ // Extract configuration
18
+ state.predicate = spec.predicate;
19
+ state.timestampField = spec.timestampField || null;
20
+
21
+ // Config copy
22
+ state.stats = spec.stats;
23
+
24
+ // Track if we need duration tracking
25
+ state.needsDurationTracking = Boolean(
26
+ spec.stats.dwellTime ||
27
+ spec.stats.dwellSamples ||
28
+ spec.stats.dutyCycle
29
+ );
30
+
31
+ // State variables - clear boolean semantics
32
+ state.active = false; // Current state
33
+ state.wasActive = false; // Previous state (for edge detection)
34
+
35
+ // Duration tracking state
36
+ state.stateEnteredAt = null; // When current state was entered (milliseconds)
37
+ state.dwellTime = null; // Duration of previous state (milliseconds)
38
+ state.dwellSamples = null; // Samples in the previous state
39
+ state.sampleCount = 0;
40
+ // Duty cycle tracking mechanism
41
+ state.dutyCycleTracker = Object.create( null );
42
+ // It is indexed by `wasActive` state as dwellTime is always for wasActive state (see update.js)
43
+ state.dutyCycleTracker.true = null;
44
+ state.dutyCycleTracker.false = null;
45
+ state.dutyCycle = null; // Used for publish duty cycle
46
+
47
+ // First message flag
48
+ state.hasSeenFirstValue = false;
49
+
50
+ state.nodeType = introspect.getNodeType();
51
+ state.inControlPhase = false;
52
+
53
+ // Log suppression: one log per error episode; reset on recovery.
54
+ state.predicateErrorLogged = false;
55
+
56
+ return state;
57
+ }; // init()
58
+
59
+ export default init;
@@ -0,0 +1,137 @@
1
+ // nodes/dwell-time-tracker/introspect.js
2
+
3
+ import { validators, composerValidators } from '../../core/utils/validate/index.js';
4
+
5
+ /** Single source of truth for what this plugin can do */
6
+ const SUPPORTED_STATS = [
7
+ 'active', // Current boolean state (passthrough)
8
+ 'dwellTime', // Duration of previous state (milliseconds)
9
+ 'dwellSamples', // Number of samples in previous state
10
+ 'dutyCycle' // Duty cycle ( active / ( active + inactive ) )
11
+ ];
12
+
13
+ /** Human-readable descriptions (for introspection) */
14
+ const STAT_DESCRIPTIONS = {
15
+ active: 'Current boolean state of the tracked predicate condition.',
16
+ dwellTime: 'Duration in milliseconds for which the previous state was maintained; populated only at the moment of state transition, otherwise null.',
17
+ dwellSamples: 'Number of samples processed while in the previous state; populated only at the moment of state transition, otherwise null.',
18
+ dutyCycle: 'Fraction of time spent in true state over one complete cycle (true time / total time); populated after completing a full cycle (two consecutive transitions), otherwise null.'
19
+ };
20
+
21
+
22
+ /** Control methods available for triggers */
23
+ const SUPPORTED_CONTROL_METHODS = {
24
+ reset: 'Clears accumulated state and restarts computation',
25
+ enable: 'Enables node processing',
26
+ disable: 'Disables node processing',
27
+ pause: 'Pauses node processing while keeping state visible',
28
+ unpause: 'Resumes node processing after pause'
29
+ };
30
+
31
+ /** The type of this node */
32
+ const NODE_TYPE = 'Dwell Time Tracker';
33
+
34
+ /** Node capabilities for documentation */
35
+ const CAPABILITIES = {
36
+ description: 'Tracks boolean state transitions with edge detection and dwell time measurement.',
37
+ features: [
38
+ 'Evaluates a predicate function to detect state changes based on user-defined logic.',
39
+ 'Measures dwell time in the previous state (in milliseconds).',
40
+ 'Counts dwell samples in the previous state.',
41
+ 'Identifies rising and falling edges using `dwellTime !== null` or `dwellSamples !== null` checks.',
42
+ 'Enables easy computation of the previous toggle time via `timestamp - dwellTime`.',
43
+ 'Supports both message timestamps and system time for flexible timing sources.'
44
+ ]
45
+ };
46
+
47
+
48
+ /** Spec default values */
49
+ export const DEFAULT_OPTIONS = {
50
+ // No defaults needed for this node
51
+ };
52
+
53
+ // Export functions for accessing metadata
54
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
55
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
56
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
57
+ export const getNodeType = () => NODE_TYPE;
58
+ export const getCapabilities = () => ( {
59
+ ...CAPABILITIES,
60
+ features: CAPABILITIES.features.slice()
61
+ } );
62
+
63
+ // DSL Metadata for transpilation
64
+ const DSL_METADATA = {
65
+ // Spec schema for validation
66
+ specSchema: {
67
+ nodeType: {
68
+ type: 'string',
69
+ required: true,
70
+ value: NODE_TYPE
71
+ },
72
+ name: {
73
+ type: 'string',
74
+ required: true,
75
+ validator: validators.identifier,
76
+ error: 'Name must be a valid identifier'
77
+ },
78
+ predicate: {
79
+ type: 'function',
80
+ required: true,
81
+ arity: 1,
82
+ validator: composerValidators.predicate,
83
+ error: 'Predicate must be a function accepting one parameter (msg)'
84
+ },
85
+ timestampField: {
86
+ type: 'string',
87
+ required: false,
88
+ validator: validators.noSpaces,
89
+ error: 'Timestamp field name cannot contain spaces'
90
+ },
91
+ stats: {
92
+ type: 'object',
93
+ required: true,
94
+ minProperties: 1,
95
+ propertyNames: SUPPORTED_STATS,
96
+ propertySchema: {
97
+ type: 'object',
98
+ required: true,
99
+ properties: {
100
+ storeAs: {
101
+ type: 'string',
102
+ required: true,
103
+ minLength: 1,
104
+ validator: validators.identifier,
105
+ error: 'storeAs must be a valid identifier'
106
+ }
107
+ }
108
+ }
109
+ },
110
+ triggers: {
111
+ type: 'array',
112
+ required: false,
113
+ itemSchema: {
114
+ type: 'object',
115
+ validator: composerValidators.trigger,
116
+ error: 'Invalid trigger specification'
117
+ }
118
+ }
119
+ },
120
+
121
+ // Cross-field validators
122
+ crossFieldValidators: [
123
+ composerValidators.noSelfTriggers
124
+ ],
125
+
126
+ // How to build the spec from DSL params
127
+ buildSpec: ( name, predicate, stats, options ) => ( {
128
+ nodeType: NODE_TYPE,
129
+ name,
130
+ predicate,
131
+ stats,
132
+ ...options
133
+ } ),
134
+
135
+ };
136
+
137
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,22 @@
1
+ // nodes/dwell-time-tracker/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;
@@ -0,0 +1,8 @@
1
+ // nodes/dwell-time-tracker/recompute.js
2
+
3
+ const recompute = function () {
4
+ // Nothing to recompute - dwell time tracker is stateless except for timing memory
5
+ return true;
6
+ }; // recompute()
7
+
8
+ export default recompute;
@@ -0,0 +1,23 @@
1
+ // nodes/dwell-time-tracker/reset.js
2
+
3
+ const reset = function ( state ) {
4
+ // Reset to initial conditions
5
+ state.active = false;
6
+ state.wasActive = false;
7
+ state.hasSeenFirstValue = false;
8
+ state.stateEnteredAt = null;
9
+ state.dwellTime = null;
10
+ state.dwellSamples = null;
11
+ state.sampleCount = 0;
12
+
13
+ state.dutyCycleTracker.true = null;
14
+ state.dutyCycleTracker.false = null;
15
+ state.dutyCycle = null;
16
+
17
+ // Clear error suppression so next error episode is logged
18
+ state.predicateErrorLogged = false;
19
+
20
+ return true;
21
+ }; // reset()
22
+
23
+ export default reset;
@@ -0,0 +1,103 @@
1
+ // nodes/dwell-time-tracker/update.js
2
+
3
+ import { executeTriggers } from '../../core/utils/node/index.js';
4
+
5
+ const update = function ( state, msg ) {
6
+ if ( state.disable || state.pause ) return state;
7
+ // Reset on each update
8
+ state.inputValidationFailed = false;
9
+
10
+ // Evaluate the predicate to compute the value
11
+ let value;
12
+
13
+ try {
14
+ value = state.predicate( msg );
15
+ // Recovery: clear log-suppression flag on success
16
+ if ( state.predicateErrorLogged ) state.predicateErrorLogged = false;
17
+ } catch ( error ) {
18
+ // Signals publishing NaN for all demanded `stats` in publishTo.
19
+ state.inputValidationFailed = true;
20
+ // Log first error per episode; suppress subsequent until recovery
21
+ if ( !state.predicateErrorLogged ) {
22
+ state.predicateErrorLogged = true;
23
+ console.error( `WinkComposer/dwellTimeTracker: predicate threw exception: ${error.message}` );
24
+ }
25
+ return state;
26
+ }
27
+ // Skip if not a valid boolean
28
+ if ( typeof value !== 'boolean' ) {
29
+ // Signals publishing NaN for all demanded `stats` in publishTo.
30
+ state.inputValidationFailed = true;
31
+ return state;
32
+ }
33
+
34
+ // Get timestamp (from message or current time)
35
+ const now = state.timestampField ? msg[ state.timestampField ] : Date.now();
36
+ // Handle time related faults gracefully: ensure their isolation
37
+ if ( !Number.isFinite( now ) ) {
38
+ // Signals publishing NaN for all demanded `stats` in publishTo.
39
+ state.inputValidationFailed = true;
40
+ return state;
41
+ }
42
+
43
+ // Remember previous state for edge detection
44
+ state.wasActive = state.active;
45
+
46
+ // Clear edge flags (they're momentary - true for one cycle only)
47
+ state.dwellTime = null; // Clear duration (only set on state change)
48
+ state.dwellSamples = null; // Clear samples.
49
+ state.dutyCycle = null; // Clear duty cycle
50
+
51
+ // Update active state (passthrough)
52
+ if ( state.hasSeenFirstValue ) {
53
+ state.active = value;
54
+ } else {
55
+ // First value - set initial state
56
+ state.active = value;
57
+ state.hasSeenFirstValue = true;
58
+ state.sampleCount = 1; // Seeing the very first sample
59
+ // Initialize state entry time ONLY for first value so that
60
+ // on change the first known value of time can be published
61
+ if ( state.needsDurationTracking ) {
62
+ state.stateEnteredAt = now;
63
+ }
64
+ // Since this is the first message, no need to check transition: simply return.
65
+ return state;
66
+ }
67
+
68
+ // Detect edges and calculate duration
69
+ if ( state.active !== state.wasActive ) { // eslint-disable-line no-negated-condition
70
+ // Calculate duration and save entry timestamp
71
+ if ( state.needsDurationTracking ) {
72
+ if ( state.stateEnteredAt !== null ) {
73
+ state.dwellTime = now - state.stateEnteredAt;
74
+ if ( state.dwellTime < 0 ) state.dwellTime = 0;
75
+ // Save for publishing
76
+ state.dwellSamples = state.sampleCount;
77
+
78
+ // Store current dwell time FIRST: Update the tracker by `wasActive` index.
79
+ state.dutyCycleTracker[ state.wasActive ] = state.dwellTime;
80
+
81
+ if ( state.dutyCycleTracker.true !== null && state.dutyCycleTracker.false !== null ) {
82
+ // Both on and off times accumulated - compute duty cycle for complete cycle
83
+ const totalDuration = state.dutyCycleTracker.true + state.dutyCycleTracker.false;
84
+ // Guard: zero total duration (identical timestamps) — NaN signals invalid computation
85
+ state.dutyCycle = ( totalDuration > 0 ) ? ( state.dutyCycleTracker.true / totalDuration ) : NaN;
86
+ // Reset tracker
87
+ state.dutyCycleTracker.true = null;
88
+ state.dutyCycleTracker.false = null;
89
+ }
90
+ }
91
+ state.stateEnteredAt = now; // Update for new state
92
+ }
93
+
94
+ // First sample in the changed state
95
+ state.sampleCount = 1;
96
+ // Trigger controls on state change
97
+ executeTriggers( state );
98
+ } else state.sampleCount += 1; // Count samples as long as state is unchanged
99
+
100
+ return state;
101
+ }; // update()
102
+
103
+ export default update;
@@ -0,0 +1,94 @@
1
+ /**
2
+ * @fileoverview Helper functions for emitIf node
3
+ *
4
+ * Status signal emission for predicate error state transitions, and the
5
+ * shared publish-failure recording used by both the data path (update.js)
6
+ * and the status-signal path below.
7
+ */
8
+
9
+ /**
10
+ * Static fallback error the framework substitutes when an adapter breaks
11
+ * the return contract — no result object at all, or `{ ok: false }` with
12
+ * no `error` (ADR-018 error vocabulary). A module-level singleton, so the
13
+ * hot path allocates nothing. Mirrors persist-if.
14
+ */
15
+ const MALFORMED_RESULT_ERROR = {
16
+ code: 'MALFORMED_RESULT',
17
+ message: 'emitter returned a malformed publish result (expected { ok, error? })'
18
+ };
19
+
20
+ /**
21
+ * Throws when an annotate return is not a plain object — arrays pass a
22
+ * bare typeof check but are just as wrong. A flow-authoring bug surfaces
23
+ * in the node's own error episode (the predicate/annotate try in
24
+ * update.js), never downstream where it would masquerade as an adapter
25
+ * failure. Mirrors persist-if.
26
+ *
27
+ * @param {*} record - The annotate return value
28
+ * @throws {Error} Naming the offending type
29
+ */
30
+ const assertAnnotateReturn = function ( record ) {
31
+ const got = record === null ? 'null' :
32
+ ( Array.isArray( record ) ? 'array' : typeof record );
33
+ if ( got === 'object' ) {
34
+ return;
35
+ }
36
+ throw new Error( `annotate must return an object, got ${got}` );
37
+ }; // assertAnnotateReturn()
38
+
39
+ /**
40
+ * Records a failed publish into the node's failure episode: updates the
41
+ * last* fields, and on the first failure of an episode captures the first*
42
+ * fields and logs one console.error (repeats stay quiet until a successful
43
+ * publish closes the episode — see update.js).
44
+ *
45
+ * @param {Object} state - Node state
46
+ * @param {Object} error - The `{ code, message }` from the emitter's
47
+ * `{ ok: false }` return (ADR-018 sink return contract)
48
+ */
49
+ const recordEmissionFailure = function ( state, error ) {
50
+ state.emissionErrors += 1;
51
+ state.lastEmissionError = error.message;
52
+ state.lastEmissionErrorCode = error.code;
53
+ if ( !state.emitErrorLogged ) {
54
+ state.emitErrorLogged = true;
55
+ state.firstEmissionError = error.message;
56
+ state.firstEmissionErrorCode = error.code;
57
+ console.error( state.emitErrorLogPrefix + error.code + '): ' + error.message );
58
+ }
59
+ }; // recordEmissionFailure()
60
+
61
+ /**
62
+ * Emits disable/enable status signal to the wired emitter. Published
63
+ * unconditionally (no connectivity pre-check per ADR-018 — during a
64
+ * disconnect the signal is still accepted into the emitter's in-process
65
+ * buffer); a failed publish rides the same failure episode as data
66
+ * emissions.
67
+ *
68
+ * @param {Object} state - Node state containing emitter and topic
69
+ * @param {boolean} disable - Whether to disable (true) or enable (false)
70
+ * @param {string} reason - Reason for disable
71
+ */
72
+ const emitStatusSignal = function ( state, disable, reason ) {
73
+ if ( !state.emitter ) {
74
+ return;
75
+ }
76
+
77
+ const signal = {
78
+ $disable: disable,
79
+ $reason: reason,
80
+ $timestamp: Date.now()
81
+ };
82
+
83
+ // Guarded read: this path runs inside update()'s catch block, so a
84
+ // TypeError here would escape update() and kill the pipeline. A
85
+ // malformed result counts as a failure with the static fallback
86
+ // error instead.
87
+ const result = state.emitter.publishNow( state.topic, signal );
88
+ if ( result && result.ok ) {
89
+ return;
90
+ }
91
+ recordEmissionFailure( state, ( result && result.error ) || MALFORMED_RESULT_ERROR );
92
+ }; // emitStatusSignal()
93
+
94
+ export { assertAnnotateReturn, emitStatusSignal, recordEmissionFailure, MALFORMED_RESULT_ERROR };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @fileoverview Public API for emitIf node
3
+ */
4
+
5
+ export { default as init } from './init.js';
6
+ export { default as update } from './update.js';
7
+ export { default as reset } from './reset.js';
8
+ export { default as recompute } from './recompute.js';
9
+ export { default as publishTo } from './publish-to.js';
10
+ // Re-export everything from introspection
11
+ export * from './introspect.js';
12
+
13
+ // Note: Control signals (disable/enable/pause/unpause) are intentionally
14
+ // not exported. emitIf is a side-effect terminal node — it should always
15
+ // process when the pipeline runs. Use predicate logic for conditional
16
+ // emission. Consistent with persist-if (peer terminal node).
@@ -0,0 +1,67 @@
1
+ /**
2
+ * @fileoverview Initialization for emitIf node
3
+ *
4
+ * Validates specification and creates initial state for conditional
5
+ * emission to external systems (MQTT, GPIO, terminal).
6
+ */
7
+
8
+ import { validateSpec } from '../../core/utils/node/index.js';
9
+ import * as introspect from './introspect.js';
10
+
11
+ const init = function ( spec ) {
12
+ validateSpec( spec, introspect );
13
+
14
+ // Create state after validation passes
15
+ const state = Object.create( null );
16
+
17
+ // Core configuration
18
+ state.nodeType = introspect.getNodeType();
19
+ state.name = spec.name;
20
+ state.requires = spec.requires;
21
+ state.predicate = spec.predicate;
22
+ state.target = spec.target;
23
+ state.emitter = spec.emitter;
24
+ state.annotate = spec.annotate || null;
25
+ state.insightType = spec.insightType;
26
+ // state.topic is injected by the partition manager during partition
27
+ // creation (not per-message). The topic encodes partition-scoped
28
+ // identity: edgeDeviceId/partitionId/specialization/insightType.
29
+ // This is an acceptable exception to "state shape fixed at init"
30
+ // because the partition ID is unknown until the first message arrives.
31
+ // See: partition-manager/update.js — MQTT TOPIC INJECTION block.
32
+ // Used by: update.js (emitter.publishNow) and helpers.js (status signals).
33
+
34
+ // State transition tracking (semantic — controls status signals)
35
+ state.inErrorState = false;
36
+ // Log suppression: one log per error episode; reset on recovery.
37
+ state.predicateErrorLogged = false;
38
+
39
+ // Statistics for observability
40
+ state.emissionCount = 0;
41
+ state.passCount = 0;
42
+ state.lastEmissionTime = null;
43
+
44
+ // Error tracking
45
+ state.emissionErrors = 0;
46
+ state.lastEmissionError = null;
47
+ // Mirrors persistIf: the code from the emitter's { ok: false, error:
48
+ // { code, message } } return; null when the error came from a predicate
49
+ // exception (predicate failures are not classified by the contract).
50
+ state.lastEmissionErrorCode = null;
51
+ // Publish-failure episode (mirrors persistIf's write-failure episode):
52
+ // one console.error per episode; a successful publish closes it. The
53
+ // first* fields keep the episode-opening error — in a cascade the LAST
54
+ // error is the symptom and the FIRST is the cause. They survive
55
+ // recovery for post-mortem reads and are overwritten only when the
56
+ // next episode opens.
57
+ state.emitErrorLogged = false;
58
+ state.firstEmissionError = null;
59
+ state.firstEmissionErrorCode = null;
60
+ // Static log prefix pre-built here per ADR-004 (no string building in
61
+ // update beyond the two runtime fields, and only once per episode).
62
+ state.emitErrorLogPrefix = `WinkComposer/emitIf: publish failed (node=${state.name}, insightType=${state.insightType}, code=`;
63
+
64
+ return state;
65
+ }; // init()
66
+
67
+ export default init;
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @fileoverview Introspection metadata for emitIf node
3
+ *
4
+ * Single source of truth for DSL metadata, validation schema,
5
+ * and node capabilities. Used by flow transpiler and documentation.
6
+ */
7
+
8
+ import { validators } from '../../core/utils/validate/index.js';
9
+ const SUPPORTED_STATS = []; // Pass-through node, no stats to publish
10
+
11
+ /** Human-readable descriptions (for introspection) */
12
+ const STAT_DESCRIPTIONS = {}; // No stats, no descriptions
13
+
14
+ /** Control methods available for triggers (none - side-effect only node) */
15
+ const SUPPORTED_CONTROL_METHODS = {};
16
+
17
+ /** The type of this node */
18
+ const NODE_TYPE = 'Emit If';
19
+
20
+ /** Node capabilities for documentation (kept accurate to shipping code) */
21
+ const CAPABILITIES = {
22
+ description: 'Conditionally emits messages to external targets (MQTT, terminal) without disrupting pipeline flow',
23
+ features: [
24
+ 'Pure pass-through behavior - messages continue unchanged',
25
+ 'Conditional emission based on predicate function',
26
+ 'Publishes through the emitter handle\'s publishNow and reads its classified { ok, error } result',
27
+ 'Optional annotate hook shapes the emitted payload; a non-object return is rejected into the node\'s error episode',
28
+ 'Status signals ($disable / $reason) on predicate error episodes and on recovery',
29
+ 'Publish failures open a loud error episode (one console.error per episode); the pipeline never stops',
30
+ 'Zero overhead when predicate evaluates to false'
31
+ ]
32
+ };
33
+
34
+ // Export functions for accessing metadata
35
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
36
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
37
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
38
+ export const getNodeType = () => NODE_TYPE;
39
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
40
+ export const DEFAULT_OPTIONS = {};
41
+
42
+ // DSL Metadata for transpilation
43
+ const DSL_METADATA = {
44
+ // Validation schema
45
+ specSchema: {
46
+ nodeType: {
47
+ type: 'string',
48
+ required: true,
49
+ value: NODE_TYPE
50
+ },
51
+ name: {
52
+ type: 'string',
53
+ required: true,
54
+ validator: validators.identifier,
55
+ error: 'Name must be a valid JavaScript identifier'
56
+ },
57
+ predicate: {
58
+ type: 'function',
59
+ required: true,
60
+ arity: 1,
61
+ error: 'Predicate must be a function with exactly one parameter (inputs)'
62
+ },
63
+ target: {
64
+ type: 'string',
65
+ required: true,
66
+ validator: validators.oneOf( [ 'mqtt', 'gpio', 'terminal' ] ),
67
+ error: 'Target type must be "mqtt", "gpio", or "terminal"'
68
+ },
69
+ insightType: {
70
+ type: 'string',
71
+ required: true,
72
+ error: 'Insight type must be specified for topic construction'
73
+ },
74
+ annotate: {
75
+ type: 'function',
76
+ required: false,
77
+ arity: 1,
78
+ error: 'Annotate must be a function with exactly one parameter (msg)'
79
+ },
80
+ stats: {
81
+ type: 'object',
82
+ required: false,
83
+ propertyNames: [], // No stats for pass-through node
84
+ error: 'EmitIf is a pass-through node and does not support stats'
85
+ }
86
+ },
87
+
88
+ // Cross-field validators
89
+ crossFieldValidators: [
90
+ ],
91
+
92
+ // Build spec from DSL
93
+ buildSpec: ( name, predicate, options ) => ( {
94
+ nodeType: NODE_TYPE,
95
+ name,
96
+ predicate,
97
+ ...options
98
+ } ),
99
+
100
+ };
101
+
102
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @fileoverview PublishTo function for emitIf node
3
+ *
4
+ * Pass-through node — message continues unchanged.
5
+ * No fields are added to the outgoing message.
6
+ */
7
+
8
+ const publishTo = function ( _state, _msg ) {
9
+ // Pure pass-through node - message continues unchanged
10
+ // No fields are added to the message
11
+ }; // publishTo()
12
+
13
+ export default publishTo;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @fileoverview Recompute function for emitIf node
3
+ *
4
+ * No numerical computation to stabilize.
5
+ */
6
+
7
+ const recompute = function ( _state ) {
8
+ // No numerical computation to stabilize
9
+ return true;
10
+ }; // recompute()
11
+
12
+ export default recompute;