@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,406 @@
1
+ // core/source-manager/mqtt/client.js
2
+
3
+ /**
4
+ * @fileoverview MQTT client wrapper for source adapter.
5
+ *
6
+ * Handles:
7
+ * - MQTT v5 connection with session persistence
8
+ * - Topic subscription with QoS 1
9
+ * - Deduplication via winkDedupId from user properties (ADR-022)
10
+ * - Automatic reconnection on disconnect (owned by mqtt.js, as
11
+ * ADR-018 requires — reconnection is the transport library's job)
12
+ * - Structured health/metrics reporting (via status.js)
13
+ * - Graceful shutdown with a time budget
14
+ *
15
+ * Message flow:
16
+ * MQTT broker → subscribe → extract userProperties → check dedup cache
17
+ * → if duplicate: skip, count (dedupHits — not a status event)
18
+ * → if new: decode payload → transform (optional) → onMessage(msg)
19
+ *
20
+ * Status shape — the ADR-018 core, plus this adapter's additions:
21
+ * `{status: 'green' | 'yellow' | 'red', connected, phase,
22
+ * error?: {code, message}}` is the shape every source shares;
23
+ * `msSinceLastMsg` and `note?` are MQTT-source extras.
24
+ * Phases: `starting` → `running`, transient `offline` / `reconnecting`
25
+ * while mqtt.js retries, `stopped` on stop. This source never emits
26
+ * `phase: 'errored'`: mqtt.js retries forever (the ADR-018 recovering
27
+ * posture), so there is no give-up path. A permanent outage is visible
28
+ * as a red CONNECTION_LOST whose `connected: false` never clears — a
29
+ * limit ADR-018 accepts for recovering sources: a wrong address and a
30
+ * long outage look identical from here.
31
+ *
32
+ * `err.code` vocabulary (per-adapter, documented here per ADR-018):
33
+ * - `INVALID_CONFIG` — setup-time; missing or malformed config
34
+ * field. Thrown synchronously from the factory (ADR-018 fail-fast
35
+ * setup), never emitted.
36
+ * - `DECODE_ERROR` — runtime, yellow. Two faces: a per-record
37
+ * report for every payload that cannot be decoded (skip, classify,
38
+ * continue — ADR-018), and a health flip when the decode-error ratio over
39
+ * the last 1,000 messages exceeds 1 %.
40
+ * - `CALLBACK_FAILED` — runtime, yellow. The user's `transform`
41
+ * threw; that one message is skipped (counted in `skipped`) and the
42
+ * stream continues. Fix the transform function — the report names
43
+ * the topic and the thrown message. Uniform with the CSV source
44
+ * (transform contract, 2026-07-11).
45
+ * - `SUBSCRIBE_FAILED` — runtime, red. The broker refused the
46
+ * subscription (typically ACL). Red immediately: nothing retries a
47
+ * subscribe until the next reconnect, so a deaf-but-connected source
48
+ * would otherwise look healthy forever.
49
+ * - `CONNECT_FAILED` — runtime, yellow. A transport-level error
50
+ * while the library retries (or a rare error event while running).
51
+ * Attached to the transient status once per retry streak — a storm
52
+ * of identical failures cannot flood the channel.
53
+ * - `CONNECTION_LOST` — runtime, red. Disconnected for more than
54
+ * 30 s (strictly greater) while the library keeps retrying.
55
+ * - `QUIET_PERIOD_EXCEEDED` — runtime, yellow, opt-in. No packet for
56
+ * longer than the configured `expectedQuietPeriodMs`.
57
+ *
58
+ * Metrics (optional `onMetrics`, ~1 Hz + on transitions): monotonic
59
+ * counters `{delivered, skipped, decodeErrors, reconnects, dedupHits,
60
+ * dedupMisses, dedupBypassed, dedupCacheSize}`. `dedupBypassed` counts
61
+ * messages that arrived without a `winkDedupId` — the signal that a
62
+ * publisher is not stamping ids (dedup is opt-in by construction).
63
+ *
64
+ * ASSUMPTIONS
65
+ * -----------
66
+ * 1. The broker speaks MQTT v5 and has session persistence enabled.
67
+ * The queued-while-disconnected guarantee is the BROKER's — a
68
+ * broker without persistence (default Mosquitto image) accepts
69
+ * the connection but keeps nothing (see constants.js).
70
+ * 2. mqtt.js owns reconnection entirely (ADR-018: reconnection is
71
+ * the transport library's job). This file
72
+ * never retries anything itself.
73
+ * 3. `onMessage` keeps up with the arrival rate. Delivery is
74
+ * fire-and-forget; a consumer slower than the broker backs up
75
+ * in Node's socket buffer and mqtt.js's queue, not here.
76
+ * 4. Publishers that want duplicate protection stamp `winkDedupId`
77
+ * (the emitter does). Unstamped messages pass through unfiltered
78
+ * and are counted in `dedupBypassed`.
79
+ *
80
+ * LIMITATIONS
81
+ * -----------
82
+ * 1. QoS 1 is at-least-once: duplicates WILL arrive after connection
83
+ * breaks. The dedup cache drops repeats within its time/count
84
+ * bounds (ADR-022); beyond them — or after a subscriber restart —
85
+ * QuestDB's at-rest dedup is the backstop.
86
+ * 2. No broker-level flow control: the source never delays PUBACK,
87
+ * so nothing tells the broker to slow down (an opt-in
88
+ * `flowControl` is future work, post-release).
89
+ * 3. No give-up path: mqtt.js retries forever, so a wrong broker
90
+ * address and a long outage look identical from here (a limit
91
+ * ADR-018 accepts for recovering sources) — both
92
+ * show as red CONNECTION_LOST whose `connected: false` never
93
+ * clears. The operator tells them apart, not the code.
94
+ * 4. Safe to kill and re-create: no disk state, no side effects.
95
+ * The cost of a crash is the dedup cache (see dedup.js).
96
+ *
97
+ * Performance (benchmark/mqtt-source/BASELINE.md, 2026-04-24, M4 Max;
98
+ * pre-ADR-022 dedup, whose replacement has the same O(1) per-message
99
+ * profile): composer's own decode/dedup/dispatch path sustains
100
+ * 897 k msg/s at 100 B payloads (317 k at 1 KB); end-to-end through
101
+ * Mosquitto at QoS 1 the single-process ceiling was ~12.9 k msg/s —
102
+ * owned by mqtt.js and the broker round-trip, not this code. At
103
+ * 10 k msg/s steady state every cell ran clean; a 30 M-message run
104
+ * showed no subscriber-side leak.
105
+ *
106
+ * @see src/core/source-manager/mqtt/status.js - The reporting rules
107
+ * @see src/core/emitter-manager/mqtt/emitter.js - Emitter counterpart
108
+ */
109
+
110
+ import mqtt from 'mqtt';
111
+
112
+ import {
113
+ MQTT_SOURCE_CONFIG,
114
+ QOS,
115
+ WINK_NAMESPACE,
116
+ DEFAULT_DEDUP_WINDOW_MS,
117
+ DEFAULT_DEDUP_MAX_ENTRIES,
118
+ METRICS_INTERVAL_MS
119
+ } from './constants.js';
120
+ import { createDedupCache } from './dedup.js';
121
+ import { createStatusReporter } from './status.js';
122
+
123
+ // ============================================================================
124
+ // CLIENT FACTORY
125
+ // ============================================================================
126
+
127
+ /**
128
+ * Create MQTT source client with deduplication and structured
129
+ * health/metrics reporting.
130
+ *
131
+ * @param {Object} config - Client configuration
132
+ * @param {string} config.brokerUrl - MQTT broker URL (e.g., 'mqtt://localhost:1883')
133
+ * @param {string|string[]} config.topics - Topic(s) to subscribe to (supports wildcards)
134
+ * @param {function} config.onMessage - Message handler: (message) => void
135
+ * @param {Object} [config.codec] - Codec for payload decoding (default: JSON.parse)
136
+ * @param {function} [config.transform] - Optional message transform: (msg) => transformedMsg
137
+ * @param {number} [config.dedupWindowMs=120000] - Dedup time bound (ADR-022)
138
+ * @param {number} [config.dedupMaxEntries=65536] - Dedup count cap (ADR-022)
139
+ * @param {string} [config.clientId] - MQTT client ID (auto-generated if omitted)
140
+ * @param {function} [config.onStatus] - Status callback; receives the
141
+ * structured ADR-018 status payload on every transition and per decode
142
+ * failure (see the fileoverview for the full vocabulary)
143
+ * @param {function} [config.onMetrics] - Counter-snapshot callback,
144
+ * called at ~1 Hz and on every health transition
145
+ * @param {number} [config.expectedQuietPeriodMs] - Opt-in quiet rule:
146
+ * health goes yellow when no packet arrives for longer than this
147
+ * @param {boolean} [config.cleanStart] - Override MQTT cleanStart (default: false for persistent sessions)
148
+ * @param {function} [config.mqttConnectFn] - MQTT connect function (for testing)
149
+ * @returns {function} Stop function that returns a Promise
150
+ */
151
+ const createMQTTSourceClient = function ( config ) {
152
+ const {
153
+ brokerUrl,
154
+ topics,
155
+ onMessage,
156
+ codec,
157
+ transform,
158
+ dedupWindowMs = DEFAULT_DEDUP_WINDOW_MS,
159
+ dedupMaxEntries = DEFAULT_DEDUP_MAX_ENTRIES,
160
+ clientId,
161
+ onStatus,
162
+ onMetrics,
163
+ expectedQuietPeriodMs,
164
+ cleanStart,
165
+ mqttConnectFn = mqtt.connect
166
+ } = config;
167
+
168
+ // Validate required config. Per ADR-018, setup-time throws carry
169
+ // classified err.code (INVALID_CONFIG for missing/malformed config fields).
170
+ if ( !brokerUrl ) {
171
+ const err = new Error( 'WinkComposer/mqtt-source: brokerUrl is required' );
172
+ err.code = 'INVALID_CONFIG';
173
+ throw err;
174
+ }
175
+ if ( !topics || ( Array.isArray( topics ) && topics.length === 0 ) ) {
176
+ const err = new Error( 'WinkComposer/mqtt-source: topics is required' );
177
+ err.code = 'INVALID_CONFIG';
178
+ throw err;
179
+ }
180
+ if ( typeof onMessage !== 'function' ) {
181
+ const err = new Error( 'WinkComposer/mqtt-source: onMessage must be a function' );
182
+ err.code = 'INVALID_CONFIG';
183
+ throw err;
184
+ }
185
+
186
+ // Normalize topics to array
187
+ const topicList = Array.isArray( topics ) ? topics : [ topics ];
188
+
189
+ // Create dedup cache — time-bounded, count-capped (ADR-022)
190
+ const dedup = createDedupCache( { windowMs: dedupWindowMs, maxEntries: dedupMaxEntries } );
191
+
192
+ // The status reporter owns every emission rule (status.js). It
193
+ // validates onStatus / onMetrics / expectedQuietPeriodMs itself,
194
+ // so ALL setup throws happen before any side effect below.
195
+ const reporter = createStatusReporter( {
196
+ onStatus,
197
+ onMetrics,
198
+ expectedQuietPeriodMs,
199
+ dedupSizeFn: dedup.size
200
+ } );
201
+
202
+ reporter.starting();
203
+
204
+ // Generate client ID if not provided
205
+ const generatedClientId = clientId || `wink-source-${Date.now()}`;
206
+
207
+ // A persistent session is filed at the broker under the client's
208
+ // name. The auto-generated name changes on every start, so the
209
+ // backlog the broker saved under the previous run's name is never
210
+ // delivered — it waits, unclaimed, until the session expires.
211
+ // Warn once at startup; the fix is one config line. Setup path
212
+ // only — nothing here touches the message path.
213
+ if ( !clientId && cleanStart !== true ) {
214
+ console.warn(
215
+ 'WinkComposer/mqtt-source: no clientId configured — this session ' +
216
+ `is persistent under the auto-generated name '${generatedClientId}'. ` +
217
+ 'After a restart, composer connects under a NEW name, so messages ' +
218
+ 'the broker saved during the downtime are never delivered. Set a ' +
219
+ 'fixed clientId (unique on your broker) in the source config.'
220
+ );
221
+ }
222
+
223
+ // Build MQTT options, allowing cleanStart override
224
+ const mqttOptions = {
225
+ ...MQTT_SOURCE_CONFIG,
226
+ clientId: generatedClientId
227
+ };
228
+
229
+ // Map the user-facing `cleanStart` key (MQTT 5 term) onto the
230
+ // option name mqtt.js reads (`clean`). See constants.js for the
231
+ // 2026-07-09 defect this spelling closed.
232
+ if ( cleanStart !== undefined ) {
233
+ mqttOptions.clean = cleanStart;
234
+ }
235
+
236
+ // Create MQTT client
237
+ const client = mqttConnectFn( brokerUrl, mqttOptions );
238
+
239
+ // One heartbeat per second: re-evaluates the time-based health
240
+ // rules (a silent source produces no events to evaluate on) and
241
+ // drives the onMetrics cadence. unref'd so a stopping process is
242
+ // not held alive by observability.
243
+ const cadence = setInterval( reporter.tick, METRICS_INTERVAL_MS );
244
+ cadence.unref();
245
+
246
+ // Track subscription state
247
+ let isSubscribed = false;
248
+
249
+ // ========================================================================
250
+ // EVENT HANDLERS
251
+ // ========================================================================
252
+
253
+ client.on( 'connect', function () {
254
+ reporter.connected();
255
+
256
+ // Subscribe to topics
257
+ client.subscribe( topicList, { qos: QOS }, function ( err ) {
258
+ if ( err ) {
259
+ reporter.subscribeFailed( err );
260
+ } else {
261
+ isSubscribed = true;
262
+ reporter.subscribed();
263
+ }
264
+ } );
265
+ } );
266
+
267
+ client.on( 'message', function ( topic, payload, packet ) {
268
+ // Extract dedupId from MQTT v5 user properties. Guard instead
269
+ // of `|| {}` — the fallback object would be a fresh per-message
270
+ // allocation for every publisher that stamps no properties.
271
+ const userProps = packet.properties && packet.properties.userProperties;
272
+ const dedupId = userProps ? userProps[ WINK_NAMESPACE.dedupId ] : undefined;
273
+
274
+ // Dedup is opt-in by construction (ADR-022): no id → bypass.
275
+ if ( dedupId === null || dedupId === undefined ) {
276
+ reporter.bypassed();
277
+ } else if ( dedup.isDuplicate( dedupId ) ) {
278
+ reporter.dupSkipped();
279
+ return;
280
+ } else {
281
+ reporter.idAccepted();
282
+ }
283
+
284
+ // Decode payload. A payload that cannot be decoded is skipped,
285
+ // classified, and reported per record (ADR-018) — the stream
286
+ // continues.
287
+ let message;
288
+ try {
289
+ if ( codec && typeof codec.unpack === 'function' ) {
290
+ message = codec.unpack( payload );
291
+ } else {
292
+ message = JSON.parse( payload.toString() );
293
+ }
294
+ reporter.decodeOk();
295
+ } catch ( err ) {
296
+ reporter.decodeFailed( `topic '${topic}': ${err.message} — message skipped` );
297
+ return;
298
+ }
299
+
300
+ // Attach metadata for downstream use
301
+ message._topic = topic; // eslint-disable-line no-underscore-dangle
302
+ message._dedupId = dedupId; // eslint-disable-line no-underscore-dangle
303
+
304
+ // Apply optional transform. Guarded: a throw skips this one
305
+ // message with a per-record CALLBACK_FAILED report and the
306
+ // stream continues — user code must never propagate into
307
+ // mqtt.js's event processing (transform contract, 2026-07-11).
308
+ let finalMessage;
309
+ try {
310
+ finalMessage = transform ? transform( message ) : message;
311
+ } catch ( err ) {
312
+ reporter.transformFailed( `topic '${topic}': transform threw: ${err.message} — message skipped` );
313
+ return;
314
+ }
315
+
316
+ // Deliver to handler
317
+ if ( finalMessage !== null && finalMessage !== undefined ) {
318
+ onMessage( finalMessage );
319
+ reporter.delivered();
320
+ } else {
321
+ reporter.transformDropped();
322
+ }
323
+ } );
324
+
325
+ client.on( 'offline', function () {
326
+ isSubscribed = false;
327
+ reporter.offline();
328
+ } );
329
+
330
+ client.on( 'error', function ( err ) {
331
+ reporter.connectError( err );
332
+ } );
333
+
334
+ client.on( 'reconnect', function () {
335
+ reporter.reconnecting();
336
+ } );
337
+
338
+ // ========================================================================
339
+ // STOP FUNCTION
340
+ // ========================================================================
341
+
342
+ /**
343
+ * Stop the MQTT client, with a time budget.
344
+ *
345
+ * Per ADR-018, source stop functions take a `{ timeout }`. We
346
+ * first ask the broker for a clean disconnect (`client.end(false)`),
347
+ * which waits for any messages still being sent. If that does not
348
+ * finish within `timeout` ms, we ask for a forced disconnect
349
+ * (`client.end(true)`), which closes the socket right away — so the
350
+ * rest of the shutdown can proceed even if the broker is slow or
351
+ * unreachable. The forced path is reported with the sources' shared
352
+ * `note` convention.
353
+ *
354
+ * The timer is `unref()`ed so it does not keep Node alive while a
355
+ * clean disconnect is in progress.
356
+ *
357
+ * Default time budget (5000 ms) matches what sinks use.
358
+ *
359
+ * @param {Object} [options] - Stop options
360
+ * @param {number} [options.timeout=5000] - Max ms to wait for clean disconnect
361
+ * @returns {Promise<void>} Resolves once the client is closed (clean or forced)
362
+ */
363
+ const stop = function ( { timeout = 5000 } = {} ) {
364
+ clearInterval( cadence );
365
+ return new Promise( function ( resolve ) {
366
+ let settled = false;
367
+ let forceTimer = null;
368
+ const onClosed = function () {
369
+ if ( settled ) return;
370
+ settled = true;
371
+ if ( forceTimer ) clearTimeout( forceTimer );
372
+ reporter.stopped();
373
+ resolve();
374
+ };
375
+ forceTimer = setTimeout( function () {
376
+
377
+ /* c8 ignore next -- defensive: settle always clears this timer (it is assigned before client.end can call back), so reaching here settled requires the timer callback to already sit in the event queue when settle runs — a race with no producer in the current design. Same guard shape as the testHarness stop. */
378
+ if ( settled ) return;
379
+ reporter.stopForced( timeout );
380
+ client.end( true, {}, onClosed );
381
+ }, timeout );
382
+ // Don't keep Node alive on the timer while the broker is
383
+ // closing cleanly. Either path resolves the Promise.
384
+ forceTimer.unref();
385
+ client.end( false, {}, onClosed );
386
+ } );
387
+ };
388
+
389
+ // Expose for testing
390
+ /* eslint-disable no-underscore-dangle */
391
+ stop._client = client;
392
+ stop._dedup = dedup;
393
+ stop._isSubscribed = function () {
394
+ return isSubscribed;
395
+ };
396
+ stop._metrics = reporter.snapshot;
397
+ /* eslint-enable no-underscore-dangle */
398
+
399
+ return stop;
400
+ };
401
+
402
+ // ============================================================================
403
+ // EXPORTS
404
+ // ============================================================================
405
+
406
+ export { createMQTTSourceClient };
@@ -0,0 +1,147 @@
1
+ // core/source-manager/mqtt/constants.js
2
+
3
+ /**
4
+ * @fileoverview Constants for MQTT source adapter.
5
+ *
6
+ * Protocol invariants (QoS, WINK_NAMESPACE, protocol version) are imported
7
+ * from the shared mqtt-protocol module. Deployment-configurable values
8
+ * (keepalive, reconnect, etc.) come from ENV_VARS.
9
+ *
10
+ * ASSUMPTIONS
11
+ * -----------
12
+ * 1. Environment variables were validated at import time by
13
+ * env-vars.js — every value here is already a sane number.
14
+ * 2. These are deployment knobs, not per-flow settings: two sources
15
+ * in one process share them (per-flow overrides go through the
16
+ * source config, e.g. dedupWindowMs).
17
+ *
18
+ * LIMITATIONS
19
+ * -----------
20
+ * 1. The persistent-session guarantee (`clean: false`) is only as
21
+ * real as the broker's persistence setting — the default
22
+ * Mosquitto image accepts the session and keeps nothing. See the
23
+ * MQTT_SOURCE_CONFIG note below.
24
+ *
25
+ * @see src/core/mqtt-protocol.js - Shared protocol invariants
26
+ * @see src/core/env-vars.js - Deployment configuration
27
+ * @see src/core/emitter-manager/mqtt/constants.js - Emitter counterpart
28
+ */
29
+
30
+ import { ENV_VARS } from '../../env-vars.js';
31
+ import {
32
+ WINK_NAMESPACE,
33
+ MQTT_QOS as QOS,
34
+ MQTT_PROTOCOL_V
35
+ } from '../../mqtt-protocol.js';
36
+
37
+ // Re-export protocol invariants for local consumers
38
+ export { WINK_NAMESPACE, QOS };
39
+
40
+ // ============================================================================
41
+ // DEDUPLICATION DEFAULTS (ADR-022)
42
+ // ============================================================================
43
+
44
+ /**
45
+ * Default dedup time bound: an entry expires this many milliseconds
46
+ * after arrival. Two minutes covers typical broker retry windows —
47
+ * a duplicate arrives roughly one reconnect gap after its original.
48
+ * Override via MQTT_SOURCE_DEDUP_WINDOW_MS.
49
+ *
50
+ * @type {number}
51
+ */
52
+ export const DEFAULT_DEDUP_WINDOW_MS = ENV_VARS.mqttSourceDedupWindowMs;
53
+
54
+ /**
55
+ * Default dedup count cap: the memory guarantee (~8 MB worst case at
56
+ * 65,536 retained UUIDs). When the cap binds before the time window,
57
+ * the effective window is maxEntries ÷ message rate — the arithmetic
58
+ * is documented in ADR-022. Override via MQTT_SOURCE_DEDUP_MAX_ENTRIES.
59
+ *
60
+ * @type {number}
61
+ */
62
+ export const DEFAULT_DEDUP_MAX_ENTRIES = ENV_VARS.mqttSourceDedupMaxEntries;
63
+
64
+ // ============================================================================
65
+ // HEALTH & METRICS
66
+ // ============================================================================
67
+
68
+ /**
69
+ * How long the source may sit disconnected before its health status
70
+ * escalates from yellow to red. The transport library keeps retrying
71
+ * either way (the ADR-018 recovering posture) — red is the operator signal
72
+ * that the outage has outlived a routine blip. Strictly greater-than:
73
+ * exactly 30,000 ms is still yellow.
74
+ *
75
+ * @type {number}
76
+ */
77
+ export const DISCONNECT_RED_MS = 30000;
78
+
79
+ /**
80
+ * How many recent decode outcomes the health check remembers. The
81
+ * yellow rule reads "more than 1 % decode errors over the last 1,000
82
+ * messages"; the ring is pre-allocated once (1 KB) so the hot path
83
+ * allocates nothing.
84
+ *
85
+ * @type {number}
86
+ */
87
+ export const DECODE_RING_SIZE = 1000;
88
+
89
+ /**
90
+ * Cadence of the health/metrics timer: once per second the client
91
+ * re-evaluates the time-based health rules and, when the caller
92
+ * supplied `onMetrics`, emits a counter snapshot. One unref'd
93
+ * interval per source instance — the only timer this adapter owns.
94
+ *
95
+ * @type {number}
96
+ */
97
+ export const METRICS_INTERVAL_MS = 1000;
98
+
99
+ // ============================================================================
100
+ // MQTT CLIENT CONFIGURATION
101
+ // ============================================================================
102
+
103
+ /**
104
+ * Default MQTT client configuration for source adapter.
105
+ * Uses MQTT v5 with session persistence for reliable message delivery.
106
+ *
107
+ * SESSION PERSISTENCE (cleanStart: false)
108
+ * ----------------------------------------
109
+ * When cleanStart is false, the broker maintains the client's session state
110
+ * (subscriptions and queued messages) across disconnections. This means:
111
+ *
112
+ * - Messages published while disconnected are queued by the broker
113
+ * - On reconnect, queued messages are delivered automatically
114
+ * - No telemetry data is lost during brief network interruptions
115
+ *
116
+ * This is the right default for production IoT/telemetry systems where
117
+ * data loss is unacceptable.
118
+ *
119
+ * BROKER REQUIREMENT
120
+ * ------------------
121
+ * Session persistence requires the MQTT broker to be configured with
122
+ * persistence enabled. The default Eclipse Mosquitto docker image does
123
+ * NOT have persistence enabled - it will accept the connection but won't
124
+ * actually persist sessions.
125
+ *
126
+ * For testing with default Mosquitto, pass `cleanStart: true` to the
127
+ * source config to use fresh sessions (no persistence requirement).
128
+ *
129
+ * @type {Object}
130
+ */
131
+ export const MQTT_SOURCE_CONFIG = {
132
+ protocolVersion: MQTT_PROTOCOL_V,
133
+ keepalive: ENV_VARS.mqttKeepalive,
134
+ reconnectPeriod: ENV_VARS.mqttReconnectMs,
135
+ connectTimeout: ENV_VARS.mqttConnectTimeoutMs,
136
+ // mqtt.js's option name is `clean` — it maps to the MQTT 5 Clean
137
+ // Start flag. The source's user-facing config key stays
138
+ // `cleanStart` (the MQTT 5 term); client.js maps it onto this.
139
+ // Before 2026-07-09 this was spelled `cleanStart`, which mqtt.js
140
+ // silently ignored — every connection ran clean=true and no
141
+ // session ever survived a reconnect. Caught by the ADR-022 dedup
142
+ // soak's forced-reconnect test.
143
+ clean: false,
144
+ properties: {
145
+ sessionExpiryInterval: ENV_VARS.mqttSessionExpiryS
146
+ }
147
+ };