@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,102 @@
1
+ // Helper: Check value against range
2
+ const checkRange = function ( state, value, rangeSpec ) {
3
+ if ( !state.hasRange || !rangeSpec ) {
4
+ return true; // No range configured, pass through
5
+ }
6
+
7
+ return value >= rangeSpec.min && value <= rangeSpec.max;
8
+ }; // checkRange()
9
+
10
+ // Helper: Check value against list
11
+ const checkValueList = function ( state, value ) {
12
+ if ( !state.valueSet ) {
13
+ return true; // No list configured, pass through
14
+ }
15
+
16
+ const isInList = state.valueSet.has( value );
17
+ // If containsValidValues is true, value must be in list (allow list)
18
+ // If containsValidValues is false, value must NOT be in list (deny list)
19
+ return state.containsValidValues ? isInList : !isInList;
20
+ }; // checkValueList()
21
+
22
+ // Helper: Check custom predicate
23
+ const checkPredicate = function ( state, value, msg ) {
24
+ if ( !state.predicate ) {
25
+ return true; // No predicate configured, pass through
26
+ }
27
+
28
+ try {
29
+ const result = state.predicate( value, msg ) === true;
30
+ // Recovery: clear log-suppression flag on success
31
+ if ( state.predicateErrorLogged ) state.predicateErrorLogged = false;
32
+ return result;
33
+ } catch ( error ) {
34
+ // Log first error per episode; suppress subsequent until recovery
35
+ if ( !state.predicateErrorLogged ) {
36
+ state.predicateErrorLogged = true;
37
+ console.error( `WinkComposer/sanitize: predicate threw exception: ${error.message}` );
38
+ }
39
+ return false;
40
+ }
41
+ }; // checkPredicate()
42
+
43
+ // Main update function
44
+ const update = function ( state, msg ) {
45
+ if ( state.disable || state.pause ) return state;
46
+
47
+ const key = state.x;
48
+ const value = msg[ key ];
49
+
50
+ // Use self compare NaN NaN check first and not the Number.isFinite(),
51
+ // as incoming value may not be number type
52
+ if ( value !== value ) { // eslint-disable-line no-self-compare
53
+ // Assume it to be a value related failure!
54
+ state.failureReason = state.REASON_VALUE_LIST;
55
+ state.failedValue = value;
56
+ return state;
57
+ }
58
+
59
+ // Resolve range tunable for this message (if configured)
60
+ if ( state.rangesFn ) {
61
+ try {
62
+ state.resolvedRangeSpec = state.rangesFn( msg );
63
+ if ( state.tunableErrorLogged ) state.tunableErrorLogged = false;
64
+ } catch ( error ) {
65
+ if ( !state.tunableErrorLogged ) {
66
+ state.tunableErrorLogged = true;
67
+ console.error( `WinkComposer/${state.nodeType}: tunable threw: ${error.message}` );
68
+ }
69
+ }
70
+ }
71
+
72
+ // Check range first (most common validation)
73
+ if ( !checkRange( state, value, state.resolvedRangeSpec ) ) {
74
+ state.failureReason = state.REASON_RANGE;
75
+ state.failedValue = value;
76
+ msg[ key ] = NaN;
77
+ return state;
78
+ }
79
+
80
+ // Check value list
81
+ if ( !checkValueList( state, value ) ) {
82
+ state.failureReason = state.REASON_VALUE_LIST;
83
+ state.failedValue = value;
84
+ msg[ key ] = NaN;
85
+ return state;
86
+ }
87
+
88
+ // Check predicate
89
+ if ( !checkPredicate( state, value, msg ) ) {
90
+ state.failureReason = state.REASON_PREDICATE;
91
+ state.failedValue = value;
92
+ msg[ key ] = NaN;
93
+ return state;
94
+ }
95
+
96
+ // Value is valid, pass through unchanged
97
+ state.failureReason = null;
98
+ state.failedValue = null;
99
+ return state;
100
+ }; // update()
101
+
102
+ export default update;
@@ -0,0 +1,14 @@
1
+ // nodes/spike-guard/index.js
2
+
3
+ export { default as init } from './init.js';
4
+ export { default as update } from './update.js';
5
+ export { default as reset } from './reset.js';
6
+ export { default as recompute } from './recompute.js';
7
+ export { default as publishTo } from './publish-to.js';
8
+ // Direct imports for common control functions
9
+ export { default as disable } from '../../core/utils/node/disable.js';
10
+ export { default as enable } from '../../core/utils/node/enable.js';
11
+ export { default as pause } from '../../core/utils/node/pause.js';
12
+ export { default as unpause } from '../../core/utils/node/unpause.js';
13
+ // Re-export everything from introspection.js
14
+ export * from './introspect.js';
@@ -0,0 +1,47 @@
1
+ // nodes/spike-guard/init.js
2
+
3
+ /**
4
+ * @fileoverview Initialize spikeGuard node state.
5
+ *
6
+ * Uses count-sliding ring buffer for 3-sample window.
7
+ * Unlike failed glitchGuard, stores VALUES not message references.
8
+ */
9
+
10
+ import { validateSpec } from '../../core/utils/node/index.js';
11
+ import * as introspect from './introspect.js';
12
+ import { create } from '../../windowing/count-sliding/index.js';
13
+
14
+ const init = function ( spec ) {
15
+ validateSpec( spec, introspect );
16
+
17
+ // Create state after validation passes
18
+ const state = Object.create( null );
19
+
20
+ // Standard input parameter health flag during update: false means healthy
21
+ state.inputValidationFailed = false;
22
+
23
+ // Ensure node is enabled by default
24
+ state.disable = false;
25
+ state.pause = false;
26
+
27
+ // Extract field name and threshold
28
+ state.x = spec.from.x;
29
+ state.threshold = spec.threshold;
30
+
31
+ // Ring buffer for 3 values (reuses count-sliding)
32
+ state.ring = create( 3 );
33
+
34
+ // Config copy for publishTo
35
+ state.stats = spec.stats;
36
+
37
+ // Output state variables
38
+ state.clean = 0;
39
+ state.detected = false;
40
+ state.magnitude = 0;
41
+
42
+ state.nodeType = introspect.getNodeType();
43
+
44
+ return state;
45
+ }; // init()
46
+
47
+ export default init;
@@ -0,0 +1,130 @@
1
+ // nodes/spike-guard/introspect.js
2
+
3
+ /**
4
+ * @fileoverview Introspection metadata for spikeGuard node.
5
+ *
6
+ * spikeGuard detects single-sample spikes using a 3-sample sliding window.
7
+ * Core insight: In window [left, middle, right]:
8
+ * - Spike: middle differs from BOTH left and right by > threshold
9
+ * - Transition: middle differs from only ONE neighbor
10
+ *
11
+ * This elegantly discriminates spikes from transitions without lookahead.
12
+ */
13
+
14
+ import { validators } from '../../core/utils/validate/index.js';
15
+
16
+ /** Single source of truth for what this plugin can do */
17
+ const SUPPORTED_STATS = [
18
+ 'clean', // Cleaned value (median of window)
19
+ 'detected', // Boolean: true when spike detected
20
+ 'magnitude' // Signed spike magnitude (negative=dip, positive=surge), 0 if no spike
21
+ ];
22
+
23
+ /** Human-readable descriptions (for introspection) */
24
+ const STAT_DESCRIPTIONS = {
25
+ clean: 'Cleaned value (median of 3-sample window)',
26
+ detected: 'Boolean: true when spike detected',
27
+ magnitude: 'Signed spike magnitude: negative=dip (dropout), positive=surge (noise), 0 if no spike'
28
+ };
29
+
30
+ /** Control methods available for triggers */
31
+ const SUPPORTED_CONTROL_METHODS = {
32
+ reset: 'Clears accumulated state and restarts computation',
33
+ enable: 'Enables node processing',
34
+ disable: 'Disables node processing',
35
+ pause: 'Pauses node processing while keeping state visible',
36
+ unpause: 'Resumes node processing after pause'
37
+ };
38
+
39
+ /** The type of this node */
40
+ const NODE_TYPE = 'Spike Guard';
41
+
42
+ /** Node capabilities for documentation */
43
+ const CAPABILITIES = {
44
+ description: 'Detects single-sample spikes and outputs cleaned (median) value',
45
+ features: [
46
+ 'Rolling window of exactly 3 values',
47
+ 'Spike = middle differs from BOTH neighbors by > threshold',
48
+ 'Outputs cleaned value (median) and detection flag',
49
+ 'Zero false positives at state transitions'
50
+ ]
51
+ };
52
+
53
+ // Export functions for accessing metadata
54
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
55
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
56
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
57
+ export const getNodeType = () => NODE_TYPE;
58
+ export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
59
+
60
+ // DSL Metadata for transpilation
61
+ // Pattern: NAME_X_OUTPUTS_OPTIONS -> .spikeGuard( name, x, stats, options )
62
+ const DSL_METADATA = {
63
+ // Spec schema for validation
64
+ specSchema: {
65
+ nodeType: {
66
+ type: 'string',
67
+ required: true,
68
+ value: NODE_TYPE
69
+ },
70
+ name: {
71
+ type: 'string',
72
+ required: true,
73
+ validator: validators.identifier,
74
+ error: 'Name must be a valid identifier'
75
+ },
76
+ from: {
77
+ type: 'object',
78
+ required: true,
79
+ properties: {
80
+ x: {
81
+ type: 'string',
82
+ required: true,
83
+ validator: validators.noSpaces,
84
+ error: 'Field name cannot contain spaces'
85
+ }
86
+ }
87
+ },
88
+ threshold: {
89
+ type: 'number',
90
+ required: true,
91
+ validator: validators.positive,
92
+ error: 'Threshold must be a positive number'
93
+ },
94
+ stats: {
95
+ type: 'object',
96
+ required: true,
97
+ minProperties: 1,
98
+ propertyNames: SUPPORTED_STATS,
99
+ propertySchema: {
100
+ type: 'object',
101
+ required: true,
102
+ properties: {
103
+ storeAs: {
104
+ type: 'string',
105
+ required: true,
106
+ minLength: 1,
107
+ validator: validators.identifier,
108
+ error: 'storeAs must be a valid identifier'
109
+ }
110
+ }
111
+ }
112
+ }
113
+ },
114
+
115
+ // Cross-field validators (none needed for spikeGuard)
116
+ crossFieldValidators: [],
117
+
118
+ // How to build the spec from DSL params
119
+ // Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
120
+ buildSpec: ( name, x, stats, options ) => ( {
121
+ nodeType: NODE_TYPE,
122
+ name,
123
+ from: { x },
124
+ stats,
125
+ ...options
126
+ } )
127
+
128
+ };
129
+
130
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,35 @@
1
+ // nodes/spike-guard/publish-to.js
2
+
3
+ /**
4
+ * @fileoverview Publish spikeGuard outputs to message.
5
+ *
6
+ * Supports three stats:
7
+ * - clean: Cleaned value (median of window)
8
+ * - detected: Boolean indicating spike detected
9
+ * - magnitude: Spike magnitude (0 if no spike)
10
+ */
11
+
12
+ import { publishNaN } from '../../core/utils/node/index.js';
13
+
14
+ const publishTo = function ( state, msg ) {
15
+ if ( state.disable ) return;
16
+
17
+ // Propagate NaN if unhealthy
18
+ if ( state.inputValidationFailed ) {
19
+ publishNaN( state, msg );
20
+ return;
21
+ }
22
+
23
+ // Publish configured stats (only those requested in spec)
24
+ if ( state.stats.clean ) {
25
+ msg[ state.stats.clean.storeAs ] = state.clean;
26
+ }
27
+ if ( state.stats.detected ) {
28
+ msg[ state.stats.detected.storeAs ] = state.detected;
29
+ }
30
+ if ( state.stats.magnitude ) {
31
+ msg[ state.stats.magnitude.storeAs ] = state.magnitude;
32
+ }
33
+ }; // publishTo()
34
+
35
+ export default publishTo;
@@ -0,0 +1,14 @@
1
+ // nodes/spike-guard/recompute.js
2
+
3
+ /**
4
+ * @fileoverview Recompute for numerical stability.
5
+ *
6
+ * spikeGuard has no numerical drift - it only computes medians
7
+ * and absolute differences. No accumulation that could drift.
8
+ */
9
+
10
+ const recompute = function () {
11
+ return true;
12
+ }; // recompute()
13
+
14
+ export default recompute;
@@ -0,0 +1,19 @@
1
+ // nodes/spike-guard/reset.js
2
+
3
+ /**
4
+ * @fileoverview Reset spikeGuard node state.
5
+ *
6
+ * Clears the ring buffer and resets output values.
7
+ */
8
+
9
+ import { reset as resetRing } from '../../windowing/count-sliding/index.js';
10
+
11
+ const reset = function ( state ) {
12
+ resetRing( state.ring );
13
+ state.clean = 0;
14
+ state.detected = false;
15
+ state.magnitude = 0;
16
+ return true;
17
+ }; // reset()
18
+
19
+ export default reset;
@@ -0,0 +1,89 @@
1
+ // nodes/spike-guard/update.js
2
+
3
+ /**
4
+ * @fileoverview Core spike detection algorithm.
5
+ *
6
+ * Window: [left, middle, right] (3-sample sliding window of VALUES)
7
+ *
8
+ * On each sample:
9
+ * 1. Push new value into window
10
+ * 2. If window full:
11
+ * - leftDiff = |middle - left|
12
+ * - rightDiff = |middle - right|
13
+ * - detected = (leftDiff > threshold) AND (rightDiff > threshold)
14
+ * - clean = median(left, middle, right)
15
+ * - magnitude = (middle - avg(left, right)) if detected, else 0
16
+ * 3. Output: clean, detected, magnitude (signed: negative=dip, positive=surge)
17
+ *
18
+ * Why This Works (No False Positives at Transitions):
19
+ *
20
+ * | Scenario | Window | leftDiff | rightDiff | Both > T? | Result |
21
+ * |---------------|------------------|----------|-----------|-----------|------------|
22
+ * | Spike | [90, 0.7, 90] | 89.3 | 89.3 | YES | DETECTED |
23
+ * | Falling edge | [90, 90, 0.7] | 0 | 89.3 | NO | not spike |
24
+ * | Falling edge | [90, 0.7, 0.7] | 89.3 | 0 | NO | not spike |
25
+ * | Rising edge | [0.7, 0.7, 90] | 0 | 89.3 | NO | not spike |
26
+ * | Rising edge | [0.7, 90, 90] | 89.3 | 0 | NO | not spike |
27
+ * | Normal | [89, 91, 90] | 2 | 1 | NO | not spike |
28
+ */
29
+
30
+ import { push } from '../../windowing/count-sliding/index.js';
31
+
32
+ const update = function ( state, msg ) {
33
+ if ( state.disable || state.pause ) return state;
34
+
35
+ const xVal = msg[ state.x ];
36
+
37
+ // Reset on each update
38
+ state.inputValidationFailed = false;
39
+
40
+ // Handle faults gracefully: ensure their isolation
41
+ if ( !Number.isFinite( xVal ) ) {
42
+ // Signals publishing NaN for all demanded `stats` in publishTo.
43
+ state.inputValidationFailed = true;
44
+ return state;
45
+ }
46
+
47
+ push( state.ring, xVal );
48
+
49
+ // Need exactly 3 values for spike detection
50
+ if ( state.ring.used < 3 ) {
51
+ // Partial window: output current value, no detection
52
+ state.clean = xVal;
53
+ state.detected = false;
54
+ state.magnitude = 0;
55
+ return state;
56
+ }
57
+
58
+ // Extract window values in chronological order: [oldest, middle, newest]
59
+ // The ring buffer's head points to the NEXT slot to write (= oldest value)
60
+ const head = state.ring.head;
61
+ const left = state.ring.buffer[ head ]; // oldest
62
+ const middle = state.ring.buffer[ ( head + 1 ) % 3 ]; // middle (potential spike)
63
+ const right = state.ring.buffer[ ( head + 2 ) % 3 ]; // newest (just pushed)
64
+
65
+ // Compute differences from middle to neighbors
66
+ const leftDiff = Math.abs( middle - left );
67
+ const rightDiff = Math.abs( middle - right );
68
+
69
+ // Spike = middle differs from BOTH neighbors by > threshold
70
+ state.detected = ( leftDiff > state.threshold ) &&
71
+ ( rightDiff > state.threshold );
72
+
73
+ // Compute median for clean value (same algorithm as median3)
74
+ let a = left;
75
+ let b = middle;
76
+ let c = right;
77
+ if ( a > b ) [ a, b ] = [ b, a ];
78
+ if ( b > c ) [ b, c ] = [ c, b ];
79
+ state.clean = ( a > b ) ? a : b;
80
+
81
+ // Signed magnitude: deviation from expected (interpolated) value
82
+ // Negative = dip (dropout), Positive = surge (noise)
83
+ const expectedValue = ( left + right ) / 2;
84
+ state.magnitude = state.detected ? ( middle - expectedValue ) : 0;
85
+
86
+ return state;
87
+ }; // update()
88
+
89
+ export default update;
@@ -0,0 +1,7 @@
1
+ export { default as init } from './init.js';
2
+ export { default as update } from './update.js';
3
+ export { default as reset } from './reset.js';
4
+ export { default as recompute } from './recompute.js';
5
+ export { default as publishTo } from './publish-to.js';
6
+ // Re-export everything from introspection.js
7
+ export * from './introspect.js';
@@ -0,0 +1,105 @@
1
+ import { validateSpec } from '../../core/utils/node/index.js';
2
+ import * as introspect from './introspect.js';
3
+ import { DATA_PREFIX, MESSAGE_SUFFIX } from './utils/build-message.js';
4
+ import { createSSEHeaders, createConnection, setupConnectionHandlers } from './utils/connection-manager.js';
5
+ import { initializeEndpoints } from './utils/endpoint-initializer.js';
6
+ import { handleHealthCheck } from './utils/health-monitor.js';
7
+ import { broadcastHeartbeat } from './utils/broadcast-heartbeat.js';
8
+ import { createServer } from 'node:http';
9
+
10
+ // Simple module-level resources
11
+ let server = null;
12
+ let heartbeatInterval = null; // eslint-disable-line no-unused-vars
13
+
14
+ const handleSSEConnection = function ( state, req, res, endpointName ) {
15
+ // Check connection limit
16
+ if ( state.totalConnections >= state.maxConnections ) {
17
+ res.writeHead( 503, { 'Content-Type': 'text/plain' } );
18
+ res.end( `Max connections reached (${state.maxConnections})` );
19
+ return;
20
+ }
21
+
22
+ // Set SSE headers
23
+ res.writeHead( 200, createSSEHeaders() );
24
+
25
+ // Create and register connection
26
+ const connection = createConnection( state, res, endpointName );
27
+
28
+ console.log( `SSE ${endpointName} connected (${state.totalConnections}/${state.maxConnections})` );
29
+
30
+ // Send initial message using same format as broadcasts
31
+ const initData = `{"connected":true,"endpoint":"${endpointName}"}`;
32
+ const initMessage = `data: ${initData}\n\n`;
33
+ res.write( initMessage );
34
+
35
+ // Setup disconnect handlers
36
+ setupConnectionHandlers( state, req, connection, endpointName );
37
+ }; // handleSSEConnection()
38
+
39
+ const routeRequest = function ( state, req, res ) {
40
+ const url = req.url;
41
+
42
+ if ( url === '/health' ) {
43
+ handleHealthCheck( state, res );
44
+ return;
45
+ }
46
+
47
+ if ( url.startsWith( '/events/' ) ) {
48
+ const endpointName = url.slice( 8 ); // Remove '/events/' prefix
49
+
50
+ if ( state.connections[ endpointName ] ) {
51
+ handleSSEConnection( state, req, res, endpointName );
52
+ } else {
53
+ res.writeHead( 404, { 'Content-Type': 'text/plain' } );
54
+ res.end( `Endpoint not configured. Available: ${state.endpointNames.join( ', ' )}` );
55
+ }
56
+ return;
57
+ }
58
+
59
+ res.writeHead( 404 );
60
+ res.end( 'Not found' );
61
+ }; // routeRequest()
62
+
63
+ const init = function ( spec ) {
64
+ validateSpec( spec, introspect );
65
+
66
+ const state = Object.create( null );
67
+
68
+ // 2. Extract and store configuration
69
+ state.port = spec.port || 3000;
70
+ state.maxConnections = spec.maxConnections || 4;
71
+ state.stats = spec.stats;
72
+ state.nodeType = introspect.getNodeType();
73
+
74
+ // 3. Initialize endpoints and connections
75
+ initializeEndpoints( state );
76
+
77
+ // 4. Performance optimization: Pre-allocated buffers and constants
78
+ state.messageBuffer = Buffer.alloc( 4096 ); // 4KB reusable buffer
79
+ state.deadConnectionsPool = []; // Reusable arrays for cleanup
80
+ state.dataPrefix = DATA_PREFIX;
81
+ state.messageSuffix = MESSAGE_SUFFIX;
82
+
83
+ // 5. Initialize statistics tracking
84
+ state.totalConnections = 0;
85
+ state.totalSent = 0;
86
+ state.totalErrors = 0;
87
+
88
+ // 6. Create and start HTTP server
89
+ server = createServer( ( req, res ) => {
90
+ routeRequest( state, req, res );
91
+ } );
92
+
93
+ server.listen( state.port, () => {
94
+ console.log( `SSE Emitter listening on port ${state.port}` );
95
+ console.log( `Configured endpoints: ${state.endpointNames.join( ', ' )}` );
96
+ } );
97
+
98
+ heartbeatInterval = setInterval(() => {
99
+ broadcastHeartbeat(state);
100
+ }, 30000);
101
+
102
+ return state;
103
+ }; // init()
104
+
105
+ export default init;