@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,160 @@
1
+ // core/emitter-manager/terminal/formatters.js
2
+
3
+ /**
4
+ * @fileoverview Per-column value formatters for the terminal emitter.
5
+ *
6
+ * Same role for terminal as `questdb/writers.js` plays for QDB: takes
7
+ * an asset-class column slice at startup and returns a value-formatter
8
+ * closure pre-resolved to the per-column quantization. Hot path is a
9
+ * lookup + a quantizer call, no per-message allocation.
10
+ *
11
+ * Quantization is delegated to the shared `core/utils/quantize` helper
12
+ * — the same one QDB's `writers.js` consumes. Both sinks compute
13
+ * identical rounded values for the same input; cross-sink alignment is
14
+ * structural, not coincidental.
15
+ *
16
+ * What this file does NOT do: validate the column slice. Validation
17
+ * lives in `assert-columns.js` and runs once at startup before the
18
+ * formatter is built. By the time these helpers see the columns map,
19
+ * every `resolution` is known to be a positive finite number (or
20
+ * absent, in which case the column falls through to global precision).
21
+ *
22
+ * @see ./assert-columns.js (defensive validation that runs first)
23
+ * @see ../../utils/quantize/index.js (shared quantizer used here and by QDB)
24
+ * @see ../../storage-manager/questdb/writers.js (sister consumer)
25
+ */
26
+
27
+ import { buildResolutionQuantizer } from '../../utils/quantize/index.js';
28
+
29
+ /**
30
+ * Build a column-name → quantizer-closure map at startup from the asset
31
+ * class column slice. Only float64 columns with a defined positive
32
+ * resolution contribute; everything else falls through to the global
33
+ * precision at format time.
34
+ *
35
+ * Each quantizer applies the same formula QDB's `createFloat64Writer`
36
+ * uses, so a value declared with `resolution: R` produces the same
37
+ * rounded number in both sinks:
38
+ *
39
+ * quantized = Math.round( value * invResolution ) * resolution
40
+ * clean = +quantized.toFixed( decimalPlaces )
41
+ *
42
+ * `Math.round(v / r) * r` snaps the value to the nearest multiple of
43
+ * `r` (the resolution grid). Then `toFixed( decimalPlaces )` cleans up
44
+ * floating-point noise like `10.100000000001` → `10.1`. The closure
45
+ * pre-resolves `invResolution` and `decimalPlaces` at startup so the
46
+ * per-message hot path is one multiply, one round, one multiply, one
47
+ * `toFixed` — no allocations, no recomputation.
48
+ *
49
+ * The per-column quantizer takes precedence over the integer-passthrough
50
+ * check in the formatter — when a column declares `resolution: 5`, an
51
+ * input value of `1234` gets quantized to `1235` (the nearest multiple
52
+ * of 5), regardless of the value's `Number.isInteger` shape. This
53
+ * matches QDB's behaviour: the asset class declared the resolution,
54
+ * the data MUST be aligned to it, integer-shaped or not.
55
+ *
56
+ * Decimal places follow the same rule as QDB writers:
57
+ * - `resolution = 0.1` → 1 decimal place
58
+ * - `resolution = 0.01` → 2 decimal places
59
+ * - `resolution = 0.001` → 3 decimal places
60
+ * - `resolution = 5` → 0 decimal places (whole-number resolution)
61
+ * - `resolution = 1` → 0 decimal places (effectively passthrough)
62
+ *
63
+ * Returns `Object.create(null)` so per-column lookups never accidentally
64
+ * hit `Object.prototype` keys (e.g., a column literally named
65
+ * `'toString'` or `'hasOwnProperty'`). The keys are runtime-supplied
66
+ * column names — exactly the case the rule says use a prototype-less
67
+ * dictionary for.
68
+ *
69
+ * @param {Object} columns - the columns map from the asset class slice
70
+ * (typically `config.assetClass.columns` post wire-semantics injection)
71
+ * @returns {Object} prototype-less map of column name → `(v) → number`
72
+ */
73
+ const buildColumnQuantizers = function ( columns ) {
74
+ const map = Object.create( null );
75
+
76
+ if ( !columns || typeof columns !== 'object' ) {
77
+ return map;
78
+ }
79
+
80
+ const colNames = Object.keys( columns );
81
+ for ( let i = 0; i < colNames.length; i += 1 ) {
82
+ const colName = colNames[ i ];
83
+ const colSpec = columns[ colName ];
84
+ if ( colSpec && colSpec.type === 'float64' ) {
85
+ // Delegate to the shared quantizer. Returns null when the
86
+ // column does not need quantization (no resolution declared
87
+ // or resolution = 1); we simply skip those columns and
88
+ // they fall back to the global precision at format time.
89
+ const quantize = buildResolutionQuantizer( colSpec.resolution );
90
+ if ( quantize ) {
91
+ map[ colName ] = quantize;
92
+ }
93
+ }
94
+ }
95
+
96
+ return map;
97
+ };
98
+
99
+ /**
100
+ * Build a value formatter closure that uses each column's quantizer
101
+ * (when one was registered for the column name) and the global
102
+ * `precision` config otherwise. Receives `(key, v)` so it can look up
103
+ * the column-specific quantizer by name.
104
+ *
105
+ * Branch order in the returned formatter:
106
+ * 1. null → `'null'`
107
+ * 2. boolean → as-is
108
+ * 3. string → as-is
109
+ * 4. non-number (object, undefined, etc.) → `String( v )`
110
+ * 5. **per-column quantizer** if the column registered one (this fires
111
+ * even for integer values, because the column declared a resolution
112
+ * and the data MUST be on the resolution grid)
113
+ * 6. integer → as-is (only reached when no per-column quantizer
114
+ * applies; preserves the original behaviour for non-resolution columns)
115
+ * 7. very small non-zero (`|v| < 0.001`) → exponential notation
116
+ * using the **global** precision (intentionally; see note below)
117
+ * 8. normal decimal → `+v.toFixed( precision )` (global fallback)
118
+ *
119
+ * **Why the exponential branch keeps using global precision.** Resolution
120
+ * describes the meaningful step size on the absolute scale. For values
121
+ * below that step — say `resolution: 0.1` and `v = 0.0003` — the
122
+ * "correctly rounded" value is `0`, but printing `0` for a non-zero
123
+ * value loses diagnostic information. Terminal is a debugging tool;
124
+ * the exponential form (`3e-4`) is more useful to a human eye than
125
+ * `0`. This is an intentional carve-out, not a bug. Note that this
126
+ * branch only fires for columns WITHOUT a per-column quantizer; columns
127
+ * with one always go through the quantizer path (step 5).
128
+ *
129
+ * @param {number} precision - Default decimal places when no per-column
130
+ * quantizer is registered
131
+ * @param {Object} columnQuantizers - Map produced by
132
+ * `buildColumnQuantizers`. Pass an empty `Object.create(null)` if no
133
+ * asset class was supplied — the formatter then falls back to global
134
+ * precision for every column.
135
+ * @returns {function(string, *): *} Formatter function `(key, v) → out`
136
+ */
137
+ const createValueFormatter = function ( precision, columnQuantizers ) {
138
+ return function ( key, v ) {
139
+ if ( v === null ) return 'null';
140
+ if ( typeof v === 'boolean' ) return v;
141
+ if ( typeof v === 'string' ) return v;
142
+ if ( typeof v !== 'number' ) return String( v );
143
+
144
+ // Per-column quantizer takes precedence over every other rule:
145
+ // the column declared a resolution, the value MUST snap to the
146
+ // grid (matching what QDB will store). This is the cross-sink
147
+ // alignment guarantee. Fires for integers too — e.g.,
148
+ // resolution=5 with value=1234 quantizes to 1235.
149
+ const quantizer = columnQuantizers[ key ];
150
+ if ( quantizer ) return quantizer( v );
151
+
152
+ // No per-column quantizer beyond this point — the original
153
+ // behaviour for non-resolution columns is preserved exactly.
154
+ if ( Number.isInteger( v ) ) return v;
155
+ if ( v !== 0 && Math.abs( v ) < 0.001 ) return v.toExponential( precision );
156
+ return Number( v.toFixed( precision ) );
157
+ };
158
+ };
159
+
160
+ export { buildColumnQuantizers, createValueFormatter };
@@ -0,0 +1,341 @@
1
+ // core/emitter-manager/terminal/index.js
2
+
3
+ /**
4
+ * @fileoverview Terminal Emitter for debugging and testing.
5
+ *
6
+ * Outputs emitted messages to stdout with smart formatting:
7
+ * - Shows all fields (no filtering — emitIf already gates what's emitted)
8
+ * - Formats numbers with configurable precision (default: 2 decimal places)
9
+ * - Compact key=value format, verbose mode for full JSON
10
+ *
11
+ * Adapter contract (ADR-018, stream sink):
12
+ * - Module-level exports per the ADR's module surface: `id`,
13
+ * `configSchema`, `createEmitter`, `durabilityClass`,
14
+ * `semanticsRequirement`, and the default aggregate
15
+ * referencing the same constants.
16
+ * - Durability class: `'best-effort'` — nothing is buffered beyond the
17
+ * stdout write; there is no queue for a crash to lose, and no recovery
18
+ * commitment either.
19
+ * - `publishNow(topic, msg)` returns `{ ok: true }` synchronously. Terminal cannot
20
+ * fail (stdout is always available); the contract still requires the return shape.
21
+ * - `getPressure()` returns `0` — Terminal has no observable buffer (stdout drains
22
+ * to the kernel); a trivial sink meets the pressure obligation with a
23
+ * constant 0 (ADR-018).
24
+ * - `getHealth()` returns `{ status: 'green', connected: true, pressure: 0 }` —
25
+ * Terminal is always healthy and always "connected" to stdout (there is no
26
+ * connection to lose). Uniform semantics with MQTT emitter / QuestDB.
27
+ * - `err.code` vocabulary:
28
+ * - Setup-time (per the ADR-018 error vocabulary):
29
+ * `INVALID_CONFIG` — a float64 column in the supplied asset class
30
+ * declares a malformed `resolution` (zero, negative, NaN, Infinity,
31
+ * non-number). Thrown by `assertColumnFacts` at `createEmitter`
32
+ * time; see the column-internal facts section below.
33
+ * - Runtime: none today. Terminal has no failure modes that surface
34
+ * through `publishNow`. Future runtime codes (e.g., `STDOUT_CLOSED`)
35
+ * will be documented here when introduced.
36
+ *
37
+ * Column-internal facts consumed (per ADR-018's adapter–semantics
38
+ * alignment):
39
+ *
40
+ * Terminal's top-level dependency on the asset class is declared in the
41
+ * `semanticsRequirement` export above: just `columns`. Inside `columns`,
42
+ * terminal additionally reads ONE field per column:
43
+ *
44
+ * - `columns.*.resolution` — read by `formatters.js` (passed to the
45
+ * shared `buildResolutionQuantizer` helper in `core/utils/quantize`).
46
+ * Asserted at startup by `assert-columns.js`: when present on a
47
+ * `float64` column, must be a positive finite number; absent is fine
48
+ * (column falls back to the global `precision` config). Throws
49
+ * `INVALID_CONFIG` otherwise.
50
+ *
51
+ * Terminal does NOT require columns to declare a `type` field — it
52
+ * handles every JS value at runtime via the formatter's branch chain.
53
+ * `type === 'float64'` is checked only as a precondition for honouring
54
+ * `resolution`; non-float64 columns ignore `resolution` as dead weight.
55
+ *
56
+ * Cross-sink alignment with QuestDB: both adapters consume `resolution`
57
+ * via the same shared quantizer (`core/utils/quantize`). For the same
58
+ * input value and the same declared resolution, terminal's formatted
59
+ * number and QDB's stored number are guaranteed identical. Verified
60
+ * by the cross-sink consistency tests in `terminal-emitter.specs.js`.
61
+ *
62
+ * @example
63
+ * import { flow, csv, terminal } from '@winkjs/composer';
64
+ *
65
+ * flow('debug')
66
+ * .sanitize(...)
67
+ * .emitIf('log', () => true, { target: 'terminal' })
68
+ * .source(csv, { path: './data.csv' })
69
+ * .emitter(terminal)
70
+ * .run();
71
+ */
72
+
73
+ import { validators } from '../../utils/validate/index.js';
74
+ import { assertColumnFacts } from './assert-columns.js';
75
+ import { buildColumnQuantizers, createValueFormatter } from './formatters.js';
76
+
77
+ /**
78
+ * Emitter identifier - must match target in emitIf specs.
79
+ * @type {string}
80
+ */
81
+ export const id = 'terminal';
82
+
83
+ /**
84
+ * Crash-survival class per ADR-018. Terminal buffers nothing beyond
85
+ * the stdout write — no queue to lose, no recovery promised.
86
+ * @type {string}
87
+ */
88
+ export const durabilityClass = 'best-effort';
89
+
90
+ /**
91
+ * Capability declaration per ADR-018's adapter–semantics alignment.
92
+ *
93
+ * Terminal opts in *optionally* to receiving the asset class. When a flow
94
+ * supplies one via `.assetClass()`, the wiring layer slices it down to
95
+ * the requested fields and injects the result as `config.assetClass`.
96
+ * With `fields: [ 'columns' ]`, terminal receives `config.assetClass`
97
+ * containing just the `columns` map — the column metadata is always
98
+ * nested under the asset class container per the semantics module's
99
+ * structure (a column map cannot exist standalone; it lives inside an
100
+ * asset class).
101
+ *
102
+ * Terminal uses each column's declared `resolution` to format float
103
+ * values to the intended number of decimal places — so a value declared
104
+ * with `resolution: 0.001` prints as `23.456` instead of `23.46` (which
105
+ * is what the global `precision: 2` default would produce). Without an
106
+ * asset class, terminal falls back to the global `precision` config —
107
+ * exactly the original global-precision behaviour.
108
+ *
109
+ * `required: false` because terminal is the simplest sink and is useful
110
+ * for debugging flows that have no semantics (e.g., a quick csv →
111
+ * terminal sanity check). The capability is purely additive when
112
+ * supplied.
113
+ *
114
+ * `fields: [ 'columns' ]` because terminal only reads the columns map;
115
+ * the other top-level asset class fields (name, description,
116
+ * insightTypes) carry no formatting-relevant facts.
117
+ */
118
+ export const semanticsRequirement = {
119
+ assetClass: {
120
+ required: false,
121
+ fields: [ 'columns' ]
122
+ }
123
+ };
124
+
125
+ /**
126
+ * Singleton success result reused on every publish. Hot-path zero allocation
127
+ * per ADR-013 / ADR-004 — `{ ok: true }` would otherwise allocate per call.
128
+ * Not frozen: V8 hot paths handle plain objects more predictably, and no
129
+ * caller mutates this.
130
+ * @type {{ok: true}}
131
+ */
132
+ const RESULT_OK = { ok: true };
133
+
134
+ /**
135
+ * Singleton health snapshot reused on every getHealth() call. Terminal's
136
+ * health is constant (no transport state to track), so a single object
137
+ * suffices. Same rationale as RESULT_OK above. `pressure: 0` completes
138
+ * the ADR-018 health floor: stdout has no observable buffer, matching
139
+ * what getPressure() reports.
140
+ * @type {{status: 'green', connected: true, pressure: 0}}
141
+ */
142
+ const HEALTH_GREEN = { status: 'green', connected: true, pressure: 0 };
143
+
144
+ /**
145
+ * Configuration schema for terminal emitter validation.
146
+ * Used by flow.emitter() to validate config at DSL time.
147
+ *
148
+ * `_propertyNames` lists every accepted key — unknown keys throw at
149
+ * DSL time (the validator's only unknown-key mechanism).
150
+ * `assetClass` is deliberately absent: wire-emitters injects it from
151
+ * the flow's `.assetClass()` after DSL validation, so a user-supplied
152
+ * value would be silently overwritten — rejecting it loudly is the
153
+ * point.
154
+ *
155
+ * @type {Object}
156
+ */
157
+ export const configSchema = {
158
+ _propertyNames: [ 'verbose', 'prefix', 'precision' ],
159
+ verbose: {
160
+ type: 'boolean',
161
+ required: false,
162
+ error: 'verbose must be a boolean'
163
+ },
164
+ prefix: {
165
+ type: 'string',
166
+ required: false,
167
+ error: 'prefix must be a string'
168
+ },
169
+ precision: {
170
+ type: 'number',
171
+ required: false,
172
+ validator: validators.nonNegative,
173
+ error: 'precision must be a non-negative number'
174
+ }
175
+ };
176
+
177
+
178
+ /**
179
+ * Formats a message for compact display.
180
+ *
181
+ * Passes the field name to `formatValue` so the formatter can look up
182
+ * per-column decimal places when the column has a declared
183
+ * `resolution`. Backward compatible — formatters that ignore the key
184
+ * (none today) would still work.
185
+ *
186
+ * @param {Object} msg - Message to format
187
+ * @param {function(string, *): *} formatValue - Value formatter
188
+ * @returns {string} Formatted message string
189
+ */
190
+ const formatCompact = function ( msg, formatValue ) {
191
+ const parts = [];
192
+
193
+ for ( const [ key, value ] of Object.entries( msg ) ) {
194
+ parts.push( `${key}=${formatValue( key, value )}` );
195
+ }
196
+
197
+ return parts.join( ' ' );
198
+ };
199
+
200
+ /**
201
+ * Formats a message as pretty JSON with number formatting.
202
+ *
203
+ * @param {Object} msg - Message to format
204
+ * @param {function(string, *): *} formatValue - Value formatter
205
+ * @returns {string} Pretty JSON string
206
+ */
207
+ const formatVerbose = function ( msg, formatValue ) {
208
+ const formatted = Object.create( null );
209
+
210
+ for ( const [ key, value ] of Object.entries( msg ) ) {
211
+ // Always pass through the formatter so per-column quantizers
212
+ // get a chance to round integer-shaped values too — e.g., when
213
+ // a column declares `resolution: 5`, an integer input of 1234
214
+ // must quantize to 1235 (the nearest multiple of 5), matching
215
+ // QDB's storage. The formatter handles every JS type internally
216
+ // and returns non-numbers as-is, so this is safe to call
217
+ // unconditionally.
218
+ formatted[ key ] = formatValue( key, value );
219
+ }
220
+
221
+ return JSON.stringify( formatted, null, 2 );
222
+ };
223
+
224
+ /**
225
+ * Creates a terminal emitter instance.
226
+ *
227
+ * @param {Object} [config={}] - Emitter configuration
228
+ * @param {boolean} [config.verbose=false] - Use pretty JSON format
229
+ * @param {string} [config.prefix=''] - Optional prefix for output lines
230
+ * @param {number} [config.precision=2] - Default decimal places for floats
231
+ * that have no per-column resolution declared
232
+ * @param {Object} [config.assetClass] - Asset class slice injected by
233
+ * wire-emitters when the flow supplies one via `.assetClass()`. Per
234
+ * terminal's `semanticsRequirement`, this contains just the `columns`
235
+ * field. Each `float64` column with a declared `resolution` formats
236
+ * to its own decimal places; columns without resolution fall back to
237
+ * the global `precision` config. Without an asset class entirely,
238
+ * every column falls back — the original global-precision behaviour,
239
+ * unchanged.
240
+ * @returns {Object} Emitter instance with publishNow, getPressure, getHealth, shutdown
241
+ */
242
+ export const createEmitter = function ( config = {} ) {
243
+ const {
244
+ verbose = false,
245
+ prefix = '',
246
+ precision = 2,
247
+ assetClass = null
248
+ } = config;
249
+
250
+ // Defensive validation per ADR-018's adapter–semantics alignment:
251
+ // any resolution declared on a float64 column must be a positive
252
+ // finite number. Throws INVALID_CONFIG with a diagnostic message
253
+ // before the emitter is built, so a misconfigured flow fails at
254
+ // startup rather than producing garbage formatting later.
255
+ const columns = assetClass && assetClass.columns;
256
+ assertColumnFacts( columns );
257
+
258
+ // Pre-compute the per-column quantizer map at startup. Each entry
259
+ // is a closure built by the shared `buildResolutionQuantizer` helper
260
+ // (in core/utils/quantize) — same closure shape QDB's writers use,
261
+ // so terminal and QDB compute identical rounded values for the same
262
+ // input. An empty prototype-less map is returned when no asset
263
+ // class was supplied; the formatter then falls back to the global
264
+ // `precision` for every column. Hot path is one O(1) lookup per
265
+ // value, no allocation.
266
+ const columnQuantizers = buildColumnQuantizers( columns );
267
+
268
+ const linePrefix = prefix ? `${prefix} ` : '';
269
+ const formatValue = createValueFormatter( precision, columnQuantizers );
270
+
271
+ return {
272
+
273
+ /**
274
+ * Publishes a message to terminal.
275
+ *
276
+ * @param {string} topic - Message topic
277
+ * @param {Object} msg - Message payload
278
+ * @returns {{ok: true}} Contract return per ADR-018.
279
+ */
280
+ publishNow: function ( topic, msg ) {
281
+ const ts = new Date().toLocaleTimeString();
282
+ const header = `${linePrefix}── ${ts} ── ${topic} ──`;
283
+
284
+ console.log( header );
285
+
286
+ if ( verbose ) {
287
+ console.log( formatVerbose( msg, formatValue ) );
288
+ } else {
289
+ console.log( formatCompact( msg, formatValue ) );
290
+ }
291
+
292
+ console.log(); // Blank line for readability
293
+
294
+ return RESULT_OK;
295
+ },
296
+
297
+ /**
298
+ * Backpressure metric for the partition manager. Terminal has no
299
+ * observable buffer (stdout drains to the kernel), so always 0.
300
+ * Sync, O(1), allocation-free per ADR-018.
301
+ *
302
+ * @returns {number} 0
303
+ */
304
+ getPressure: function () {
305
+ return 0;
306
+ },
307
+
308
+ /**
309
+ * Health snapshot for operator monitoring. Terminal is always healthy
310
+ * and always "connected" to stdout — there is no transport state to
311
+ * lose. Returns the singleton HEALTH_GREEN.
312
+ *
313
+ * Uniform semantics with MQTT emitter / QuestDB:
314
+ * - `status: 'green'` — operational
315
+ * - `connected: true` — transport active (Terminal exception: always true)
316
+ * - `pressure: 0` — no observable buffer (the ADR-018 health floor)
317
+ *
318
+ * @returns {{status: 'green', connected: true, pressure: 0}}
319
+ */
320
+ getHealth: function () {
321
+ return HEALTH_GREEN;
322
+ },
323
+
324
+ /**
325
+ * No-op shutdown for terminal emitter.
326
+ *
327
+ * Accepts the ADR-018 shutdown-contract shape `{ timeout }`. Terminal has
328
+ * no body to time out (stdout drains to the kernel without our help),
329
+ * so the value is intentionally unused — the leading `_` on the
330
+ * destructured name signals that to readers and to ESLint.
331
+ *
332
+ * @param {{timeout?: number}} [_options]
333
+ * @returns {Promise<void>}
334
+ */
335
+ shutdown: async function ( { timeout: _timeout = 0 } = {} ) {
336
+ // Nothing to clean up.
337
+ }
338
+ };
339
+ };
340
+
341
+ export default { id, configSchema, durabilityClass, semanticsRequirement, createEmitter };