@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,163 @@
1
+ /**
2
+ * @fileoverview Introspection metadata for kernel node.
3
+ *
4
+ * Defines supported stats, control methods, capabilities, DSL schema,
5
+ * preset validation, default options, and custom kernel validation.
6
+ * All getters return defensive copies.
7
+ */
8
+
9
+ import { validators } from '../../core/utils/validate/index.js';
10
+ import PRESETS, { getPresetNames } from './presets.js';
11
+
12
+ /** Single source of truth for what this plugin can do */
13
+ const SUPPORTED_STATS = [
14
+ 'filtered'
15
+ ];
16
+
17
+ /** Human-readable descriptions */
18
+ const STAT_DESCRIPTIONS = {
19
+ filtered: 'Result of applying the kernel (weighted sum over sliding window)'
20
+ };
21
+
22
+ /** Control methods available for triggers */
23
+ const SUPPORTED_CONTROL_METHODS = {
24
+ reset: 'Clears accumulated state and restarts computation',
25
+ enable: 'Enables node processing',
26
+ disable: 'Disables node processing',
27
+ pause: 'Pauses node processing while keeping state visible',
28
+ unpause: 'Resumes node processing after pause'
29
+ };
30
+
31
+ /** The type of this node */
32
+ const NODE_TYPE = 'Kernel';
33
+
34
+ /** Node capabilities for documentation */
35
+ const CAPABILITIES = {
36
+ description: 'Apply weighted operations (kernels) over sliding windows in streaming data',
37
+ features: [
38
+ 'Zero-allocation circular buffer',
39
+ 'Scientifically validated presets (Savitzky-Golay, binomial, etc.)',
40
+ 'Custom kernel support',
41
+ 'Automatic time alignment'
42
+ ],
43
+ categories: {
44
+ smoothing: [ 'smooth3', 'smooth5', 'sg5', 'sg7', 'binomial5', 'binomial7' ],
45
+ derivatives: [ 'rate', 'rate3', 'accel', 'sgRate5', 'shock', 'jerk' ],
46
+ detection: [ 'spike3', 'edge5', 'impulse' ],
47
+ slope: [ 'trend5', 'trend7', 'trend9', 'trend11', 'momentum5' ],
48
+ industrial: [ 'debounce5', 'envelope', 'volatility' ]
49
+ }
50
+ };
51
+
52
+ // Export functions for accessing metadata
53
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
54
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
55
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
56
+ export const getNodeType = () => NODE_TYPE;
57
+ export const getCapabilities = () => ( {
58
+ ...CAPABILITIES,
59
+ features: CAPABILITIES.features.slice(),
60
+ categories: { ...CAPABILITIES.categories }
61
+ } );
62
+ export const getPresets = () => ( { ...PRESETS } );
63
+
64
+ /** Default options — kernel has no optional parameters. */
65
+ export const DEFAULT_OPTIONS = {};
66
+
67
+ // Custom validators
68
+ // Describes one kernel: an array of 2-100 finite numbers. The validation engine
69
+ // applies this per entry of a field-keyed map { field: [ ... ] }, so this stays a
70
+ // plain single-value check (see runCustomValidator in validate/helpers.js).
71
+ const kernelValidator = ( value ) => {
72
+ if ( !Array.isArray( value ) ) return false;
73
+ if ( value.length < 2 ) return false;
74
+ if ( value.length > 100 ) return false; // Reasonable limit
75
+ return value.every( ( v ) => Number.isFinite( v ) );
76
+ };
77
+
78
+ // DSL Metadata for transpiler
79
+ const DSL_METADATA = {
80
+ // Validation schema
81
+ specSchema: {
82
+ nodeType: {
83
+ type: 'string',
84
+ required: true,
85
+ value: NODE_TYPE
86
+ },
87
+ name: {
88
+ type: 'string',
89
+ required: true,
90
+ validator: validators.identifier,
91
+ error: 'Name must be a valid identifier'
92
+ },
93
+ from: {
94
+ type: 'object',
95
+ required: true,
96
+ properties: {
97
+ x: {
98
+ type: 'string',
99
+ required: true,
100
+ validator: validators.noSpaces,
101
+ error: 'Field name cannot contain spaces'
102
+ }
103
+ }
104
+ },
105
+ // Either preset OR kernel must be provided
106
+ preset: {
107
+ type: 'stringOrFieldKeyed',
108
+ required: false,
109
+ validator: validators.oneOf( getPresetNames() ),
110
+ error: `Preset must be one of: ${getPresetNames().join(', ')}`
111
+ },
112
+ kernel: {
113
+ type: 'arrayOrFieldKeyed',
114
+ required: false,
115
+ validator: kernelValidator,
116
+ error: 'Kernel must be an array of 2-100 numbers'
117
+ },
118
+ stats: {
119
+ type: 'object',
120
+ required: true,
121
+ minProperties: 1,
122
+ propertyNames: SUPPORTED_STATS,
123
+ propertySchema: {
124
+ type: 'object',
125
+ properties: {
126
+ storeAs: {
127
+ type: 'string',
128
+ required: true,
129
+ validator: validators.identifier,
130
+ error: 'storeAs must be a valid identifier'
131
+ }
132
+ }
133
+ }
134
+ }
135
+ },
136
+
137
+ // Cross-field validators
138
+ crossFieldValidators: [
139
+ {
140
+ fields: [ 'preset', 'kernel' ],
141
+ validator: ( spec ) => {
142
+ // Exactly one must be provided
143
+ const hasPreset = spec.preset !== undefined;
144
+ const hasKernel = spec.kernel !== undefined;
145
+ return ( hasPreset && !hasKernel ) || ( !hasPreset && hasKernel );
146
+ },
147
+ error: 'Must provide either preset or kernel, but not both'
148
+ }
149
+ ],
150
+
151
+ // Build spec from DSL
152
+ // Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
153
+ buildSpec: ( name, x, stats, options ) => ( {
154
+ nodeType: NODE_TYPE,
155
+ name,
156
+ from: { x },
157
+ stats,
158
+ ...options
159
+ } ),
160
+
161
+ };
162
+
163
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );
@@ -0,0 +1,74 @@
1
+ /**
2
+ * @fileoverview Scientifically validated filter kernels for common streaming operations.
3
+ *
4
+ * Each preset is optimized for specific use cases in industrial, financial,
5
+ * and quality control applications. Kernels are defined in newest-to-oldest
6
+ * order (kernel[0] = weight for x[n]).
7
+ */
8
+
9
+ const PRESETS = Object.create( null );
10
+ // Basic smoothing operations
11
+ PRESETS.smooth3 = [ 0.25, 0.5, 0.25 ]; // 3-point Gaussian approximation
12
+ PRESETS.smooth5 = [ 0.1, 0.2, 0.4, 0.2, 0.1 ]; // 5-point Gaussian approximation
13
+
14
+ // Derivatives
15
+ PRESETS.rate = [ -1, 1 ]; // Simple difference (1st derivative)
16
+ PRESETS.rate3 = [ -1, 0, 1 ]; // Centered difference (less noise)
17
+ PRESETS.accel = [ 1, -2, 1 ]; // 2nd derivative (acceleration)
18
+
19
+ // Savitzky-Golay filters (preserves peaks while smoothing)
20
+ PRESETS.sg5 = [ -3 / 35, 12 / 35, 17 / 35, 12 / 35, -3 / 35 ]; // 5-point, 2nd order polynomial
21
+ PRESETS.sg7 = [ -2 / 21, 3 / 21, 6 / 21, 7 / 21, 6 / 21, 3 / 21, -2 / 21 ]; // 7-point, 2nd order
22
+ PRESETS.sgRate5 = [ -2 / 10, -1 / 10, 0, 1 / 10, 2 / 10 ]; // SG 1st derivative
23
+
24
+ // Trend kernels — Savitzky-Golay 1st-derivative (slope) filters, with
25
+ // the sign chosen so that a rising signal produces a POSITIVE slope and
26
+ // a falling signal produces a NEGATIVE slope. This matches the standard
27
+ // calculus/academic convention and reads naturally in thresholding code:
28
+ // `threshold({ below: -0.06 })` fires on a downward trend.
29
+ //
30
+ // Note: the older `sgRate5` preset uses the opposite sign (falling →
31
+ // positive) because kernel weights are user-defined newest-to-oldest
32
+ // and sgRate5 pre-dates this convention discussion. The trend family
33
+ // is independent — use these when the sign should read as a slope.
34
+ //
35
+ // weights_i = −i / Σi² for i = −N..N (newest-to-oldest user order).
36
+ // Wider windows trade latency for smoothness: trend5 is the most
37
+ // responsive, trend11 the smoothest. Pair with threshold +
38
+ // persistenceCheck to detect sustained rising or falling trends.
39
+ // Reference: Savitzky & Golay (1964), Anal. Chem. 36(8), 1627.
40
+ PRESETS.trend5 = [ 2 / 10, 1 / 10, 0, -1 / 10, -2 / 10 ]; // Σi² = 10
41
+ PRESETS.trend7 = [ 3 / 28, 2 / 28, 1 / 28, 0, -1 / 28, -2 / 28, -3 / 28 ]; // Σi² = 28
42
+ PRESETS.trend9 = [ 4 / 60, 3 / 60, 2 / 60, 1 / 60, 0,
43
+ -1 / 60, -2 / 60, -3 / 60, -4 / 60 ]; // Σi² = 60
44
+ PRESETS.trend11 = [ 5 / 110, 4 / 110, 3 / 110, 2 / 110, 1 / 110, 0,
45
+ -1 / 110, -2 / 110, -3 / 110, -4 / 110, -5 / 110 ]; // Σi² = 110
46
+
47
+ // Binomial filters (optimal noise reduction)
48
+ PRESETS.binomial5 = [ 1 / 16, 4 / 16, 6 / 16, 4 / 16, 1 / 16 ]; // Pascal's triangle row 4
49
+ PRESETS.binomial7 = [ 1 / 64, 6 / 64, 15 / 64, 20 / 64, 15 / 64, 6 / 64, 1 / 64 ]; // Row 6
50
+
51
+ // Industrial process control
52
+ PRESETS.debounce5 = [ 0.1, 0.2, 0.4, 0.2, 0.1 ]; // Digital signal debouncing
53
+
54
+ // Detection filters
55
+ PRESETS.spike3 = [ -1, 2, -1 ]; // Spike/transient detection
56
+ PRESETS.edge5 = [ -1, -1, 5, -1, -1 ]; // Edge enhancement (Laplacian)
57
+ PRESETS.impulse = [ 0.25, -1, 1.5, -1, 0.25 ]; // Impulse/event detection
58
+
59
+ // Mechanical/vibration analysis
60
+ PRESETS.shock = [ 1, -2, 1 ]; // Shock/impact detection
61
+ PRESETS.jerk = [ -1, 3, -3, 1 ]; // 3rd derivative (jerk)
62
+ PRESETS.envelope = [ 0.1, 0.15, 0.5, 0.15, 0.1 ]; // Envelope extraction
63
+
64
+ // Business/financial metrics
65
+ PRESETS.momentum5 = [ -0.3, -0.1, 0, 0.1, 0.3 ]; // Weighted momentum
66
+ PRESETS.volatility = [ -1, 2, -1 ]; // Local variance proxy
67
+
68
+ export default PRESETS;
69
+
70
+ // Helper to validate preset names
71
+ export const isValidPreset = ( name ) => Object.prototype.hasOwnProperty.call( PRESETS, name );
72
+
73
+ // Get list of all preset names for validation
74
+ export const getPresetNames = () => Object.keys( PRESETS );
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview Publish function for kernel node.
3
+ *
4
+ * Copies the convolution result to the outgoing message. Gates on warmup
5
+ * (ring buffer not yet full) and input validation failure (publishes NaN).
6
+ */
7
+
8
+ import { publishNaN } from '../../core/utils/node/index.js';
9
+ import { isNotFull } from '../../windowing/count-sliding/index.js';
10
+
11
+ const publishTo = function ( state, msg ) {
12
+ if ( state.disable ) return;
13
+ // Propagate NaN if unhealthy
14
+ if ( state.inputValidationFailed ) {
15
+ publishNaN( state, msg );
16
+ return;
17
+ }
18
+
19
+ // Ring is full (warmup complete) — publish the result
20
+ if ( !isNotFull( state.ring ) ) {
21
+ msg[ state.stats.filtered.storeAs ] = state.result;
22
+ }
23
+ }; // publishTo()
24
+
25
+ export default publishTo;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @fileoverview Recompute for kernel node.
3
+ *
4
+ * No-op — returns true. The convolution result is computed fresh from ring
5
+ * buffer contents on every message, so there is no accumulator drift to
6
+ * correct. Unlike recursive filters (e.g., Kalman), FIR convolution is
7
+ * inherently stable.
8
+ */
9
+
10
+ const recompute = function () {
11
+ return true;
12
+ }; // recompute()
13
+
14
+ export default recompute;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * @fileoverview Reset for kernel node.
3
+ *
4
+ * Clears the ring buffer and result. Configuration properties (kernel,
5
+ * field names, stats) are preserved across reset.
6
+ */
7
+
8
+ import * as ring from '../../windowing/count-sliding/index.js';
9
+
10
+ const reset = function ( state ) {
11
+ // Reset window state
12
+ ring.reset( state.ring );
13
+
14
+ // Clear result
15
+ state.result = 0;
16
+
17
+ return true;
18
+ }; // reset()
19
+
20
+ export default reset;
@@ -0,0 +1,69 @@
1
+ /**
2
+ * @fileoverview Core FIR convolution for kernel node (hot path).
3
+ *
4
+ * Pushes each valid input into a doubled ring buffer — each value is written
5
+ * at `buffer[ head ]` AND `buffer[ head + size ]` so the convolution window
6
+ * is always contiguous at `buffer[ head .. head + size - 1 ]`, eliminating
7
+ * the wrap branch inside the inner loop. Push and fullness check are
8
+ * inlined. The kernel array has been reversed at init (see init.js) so the
9
+ * buffer is read oldest-to-newest with the kernel indexed 0..L-1.
10
+ *
11
+ * Zero allocations in the hot path — all arithmetic on pre-allocated
12
+ * structures created in init(). Fault isolation via `inputValidationFailed`
13
+ * flag propagates NaN downstream through publishTo().
14
+ *
15
+ * References:
16
+ * - Oppenheim & Schafer, "Discrete-Time Signal Processing", Ch. 2.3
17
+ * (Convolution Sum).
18
+ * - "Duplicated head" ring-buffer pattern (FFTW small-transform codelets,
19
+ * https://www.fftw.org/fftw-paper-ieee.pdf Sec. III.B).
20
+ * - Benchmark: 2.2-2.6× speedup vs. the prior single-size ring
21
+ * implementation (measured; ADR-012).
22
+ */
23
+
24
+ const update = function ( state, msg ) {
25
+ if ( state.disable || state.pause ) return state;
26
+ const xVal = msg[ state.x ];
27
+ // Reset on each update
28
+ state.inputValidationFailed = false;
29
+ // Handle faults gracefully: flag and continue — downstream sees NaN
30
+ if ( !Number.isFinite( xVal ) ) {
31
+ state.inputValidationFailed = true;
32
+ return state;
33
+ }
34
+
35
+ const ring = state.ring;
36
+ const size = ring.size;
37
+ const buffer = ring.buffer;
38
+ let head = ring.head;
39
+
40
+ // Doubled-buffer push: write at head AND head + size.
41
+ // Head advances within [ 0, size ); the mirror half makes the read
42
+ // window contiguous without a modulus or wrap branch.
43
+ buffer[ head ] = xVal;
44
+ buffer[ head + size ] = xVal;
45
+ head = ( head + 1 < size ) ? head + 1 : 0;
46
+ ring.head = head;
47
+
48
+ // Warmup: grow `used` up to `size`, then skip convolution while still
49
+ // partial. Once `used === size` on the current push, fall through to
50
+ // compute the first convolution output on that same message — matches
51
+ // baseline semantics (fill-then-compute on the boundary push).
52
+ if ( ring.used < size ) {
53
+ ring.used += 1;
54
+ if ( ring.used < size ) return state;
55
+ }
56
+
57
+ // Contiguous convolution — no wrap branch in the inner loop.
58
+ const kernel = state.kernel;
59
+ const kernelLength = state.kernelLength;
60
+ let sum = 0;
61
+ for ( let i = 0; i < kernelLength; i += 1 ) {
62
+ sum += buffer[ head + i ] * kernel[ i ];
63
+ }
64
+
65
+ state.result = sum;
66
+ return state;
67
+ }; // update()
68
+
69
+ export default update;
@@ -0,0 +1,21 @@
1
+ // nodes/lag/index.js
2
+
3
+ /**
4
+ * @fileoverview Exports for the lag node.
5
+ *
6
+ * The lag node computes five lag-based statistics: delta, ratio, roc,
7
+ * slope, and logReturn. This replaces and extends the former delta node.
8
+ *
9
+ * @see ADR-004 for patterns
10
+ */
11
+
12
+ export { default as init } from './init.js';
13
+ export { default as update } from './update.js';
14
+ export { default as reset } from './reset.js';
15
+ export { default as recompute } from './recompute.js';
16
+ export { default as publishTo } from './publish-to.js';
17
+ export { default as disable } from '../../core/utils/node/disable.js';
18
+ export { default as enable } from '../../core/utils/node/enable.js';
19
+ export { default as pause } from '../../core/utils/node/pause.js';
20
+ export { default as unpause } from '../../core/utils/node/unpause.js';
21
+ export * from './introspect.js';
@@ -0,0 +1,87 @@
1
+ // nodes/lag/init.js
2
+
3
+ /**
4
+ * @fileoverview Initializes the lag node state.
5
+ *
6
+ * Creates pre-allocated ring buffer(s) and pre-computes stat flags for
7
+ * zero-allocation hot path execution. Follows ADR-004 patterns.
8
+ *
9
+ * @see ADR-004
10
+ */
11
+
12
+ import { validateSpec } from '../../core/utils/node/index.js';
13
+ import * as introspect from './introspect.js';
14
+ import { create } from '../../windowing/count-sliding/index.js';
15
+ import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
16
+
17
+ /**
18
+ * Initializes the lag node state from a validated spec.
19
+ *
20
+ * @param {Object} spec - Node specification
21
+ * @param {string} spec.name - Unique node identifier
22
+ * @param {Object} spec.from - Input field configuration
23
+ * @param {string} spec.from.x - Input field name for values
24
+ * @param {string} [spec.timestamp] - Input field name for timestamps (required for slope)
25
+ * @param {number|Object} [spec.lag=1] - Lag window size (supports field-keyed)
26
+ * @param {boolean} [spec.absolute=false] - Apply Math.abs to delta and slope
27
+ * @param {Object} spec.stats - Output statistics configuration
28
+ * @returns {Object} Initialized state object
29
+ */
30
+ const init = function ( spec ) {
31
+ validateSpec( spec, introspect );
32
+
33
+ // Create state with null prototype (ADR-004: avoid prototype pollution)
34
+ const state = Object.create( null );
35
+
36
+ // ── Standard Flags ─────────────────────────────────────────────────────
37
+ state.inputValidationFailed = false;
38
+ state.disable = false;
39
+ state.pause = false;
40
+
41
+ // ── Configuration from Spec ────────────────────────────────────────────
42
+ state.x = spec.from.x;
43
+ state.timestamp = spec.timestamp ?? null;
44
+ state.stats = spec.stats;
45
+
46
+ // Resolve lag (supports field-keyed specification)
47
+ const lagSpec = resolveScalar( spec.lag, state.x );
48
+ state.lag = lagSpec ?? introspect.DEFAULT_OPTIONS.lag;
49
+
50
+ // Apply defaults for optional parameters
51
+ state.absolute = spec.absolute ?? introspect.DEFAULT_OPTIONS.absolute;
52
+
53
+ // ── Pre-allocated Ring Buffers ─────────────────────────────────────────
54
+ // Primary ring buffer for x values (always allocated)
55
+ state.ringX = create( state.lag );
56
+
57
+ // Secondary ring buffer for timestamps (only if slope is requested)
58
+ state.ringT = ( spec.stats.slope === undefined ) ? null : create( state.lag );
59
+
60
+ // ── Pre-computed Stat Flags (zero-branch hot path) ─────────────────────
61
+ // These flags avoid object property iteration in update()
62
+ state.hasDelta = spec.stats.delta !== undefined;
63
+ state.hasRatio = spec.stats.ratio !== undefined;
64
+ state.hasRoc = spec.stats.roc !== undefined;
65
+ state.hasSlope = spec.stats.slope !== undefined;
66
+ state.hasLogReturn = spec.stats.logReturn !== undefined;
67
+ state.hasCumDelta = spec.stats.cumDelta !== undefined;
68
+ state.hasXLag = spec.stats.xLag !== undefined;
69
+
70
+ // ── Computed Values (output storage) ───────────────────────────────────
71
+ // Initialize to NaN (startup state before buffer fills)
72
+ state.delta = NaN;
73
+ state.ratio = NaN;
74
+ state.roc = NaN;
75
+ state.slope = NaN;
76
+ state.logReturn = NaN;
77
+ state.xLag = NaN;
78
+ // cumDelta initializes to 0 (integral from a to a = 0)
79
+ state.cumDelta = 0;
80
+
81
+ // ── Metadata ───────────────────────────────────────────────────────────
82
+ state.nodeType = introspect.getNodeType();
83
+
84
+ return state;
85
+ }; // init()
86
+
87
+ export default init;
@@ -0,0 +1,202 @@
1
+ // nodes/lag/introspect.js
2
+
3
+ /**
4
+ * @fileoverview Introspection metadata for the lag node.
5
+ *
6
+ * The lag node computes five lag-based statistics from a single ring buffer:
7
+ * delta, ratio, roc (rate of change), slope (time-normalized), and logReturn.
8
+ * This replaces and extends the former delta node.
9
+ *
10
+ * @see ADR-004 for patterns
11
+ */
12
+
13
+ import { validators } from '../../core/utils/validate/index.js';
14
+
15
+ /**
16
+ * Supported output statistics.
17
+ * At least one must be specified in the spec.
18
+ * @type {string[]}
19
+ */
20
+ const SUPPORTED_STATS = [
21
+ 'delta',
22
+ 'ratio',
23
+ 'roc',
24
+ 'slope',
25
+ 'logReturn',
26
+ 'cumDelta',
27
+ 'xLag'
28
+ ];
29
+
30
+ /**
31
+ * Human-readable descriptions for each statistic.
32
+ * @type {Object<string, string>}
33
+ */
34
+ const STAT_DESCRIPTIONS = {
35
+ delta: 'Absolute change: x - x_lag',
36
+ ratio: 'Relative value: x / x_lag',
37
+ roc: 'Rate of change (percentage): (x - x_lag) / x_lag',
38
+ slope: 'Time-normalized change: (x - x_lag) / (t - t_lag)',
39
+ logReturn: 'Log return (continuously compounded): ln(x / x_lag)',
40
+ cumDelta: 'Cumulative delta: running sum of (x - x_lag), reset sets lower limit',
41
+ xLag: 'Lagged input value: the value pushed lag samples ago. NaN during the warmup window (first lag samples).'
42
+ };
43
+
44
+ /**
45
+ * Control methods available for triggers.
46
+ * @type {Object<string, string>}
47
+ */
48
+ const SUPPORTED_CONTROL_METHODS = {
49
+ reset: 'Resets computed values; preserves ring buffer when cumDelta is configured (ADR-008)',
50
+ enable: 'Enables node processing',
51
+ disable: 'Disables node processing',
52
+ pause: 'Pauses node processing while keeping state visible',
53
+ unpause: 'Resumes node processing after pause'
54
+ };
55
+
56
+ /**
57
+ * Node type identifier.
58
+ * @type {string}
59
+ */
60
+ const NODE_TYPE = 'Lag';
61
+
62
+ /**
63
+ * Node capabilities for documentation and introspection.
64
+ * @type {Object}
65
+ */
66
+ const CAPABILITIES = {
67
+ description: 'Computes lag-based statistics comparing current and historical values',
68
+ features: [
69
+ 'Seven statistics: delta, ratio, roc, slope, logReturn, cumDelta, xLag',
70
+ 'Configurable lag window for historical comparison',
71
+ 'Optional absolute value transformation for delta and slope',
72
+ 'Shared ring buffer for memory efficiency',
73
+ 'Optional timestamp support for slope computation',
74
+ 'Handles missing/invalid data gracefully with NaN propagation',
75
+ 'Cumulative delta for integration-style computations with reset',
76
+ 'Direct xLag publication for lookback-based downstream patterns'
77
+ ]
78
+ };
79
+
80
+ // ── Exported Getters (defensive copies) ────────────────────────────────────
81
+
82
+ export const getSupportedStats = () => SUPPORTED_STATS.slice();
83
+ export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
84
+ export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
85
+ export const getNodeType = () => NODE_TYPE;
86
+ export const getCapabilities = () => ( {
87
+ ...CAPABILITIES,
88
+ features: CAPABILITIES.features.slice()
89
+ } );
90
+
91
+ // ── Default Options ────────────────────────────────────────────────────────
92
+
93
+ /**
94
+ * Default values for optional spec parameters.
95
+ * @type {Object}
96
+ */
97
+ export const DEFAULT_OPTIONS = {
98
+ lag: 1,
99
+ absolute: false
100
+ };
101
+
102
+ // ── DSL Metadata ───────────────────────────────────────────────────────────
103
+
104
+ /**
105
+ * DSL metadata for transpilation and validation.
106
+ *
107
+ * Signature pattern: NAME_X_OUTPUTS_OPTIONS
108
+ * DSL call: .lag( name, x, stats, options )
109
+ */
110
+ const DSL_METADATA = {
111
+ specSchema: {
112
+ nodeType: {
113
+ type: 'string',
114
+ required: true,
115
+ value: NODE_TYPE
116
+ },
117
+ name: {
118
+ type: 'string',
119
+ required: true,
120
+ validator: validators.identifier,
121
+ error: 'name must be a valid identifier'
122
+ },
123
+ from: {
124
+ type: 'object',
125
+ required: true,
126
+ properties: {
127
+ x: {
128
+ type: 'string',
129
+ required: true,
130
+ validator: validators.noSpaces,
131
+ error: 'from.x must not contain spaces'
132
+ }
133
+ }
134
+ },
135
+ lag: {
136
+ type: 'numberOrFieldKeyed',
137
+ required: false,
138
+ integer: true,
139
+ min: 1,
140
+ default: DEFAULT_OPTIONS.lag
141
+ },
142
+ timestamp: {
143
+ type: 'string',
144
+ required: false,
145
+ validator: validators.noSpaces,
146
+ error: 'timestamp must not contain spaces'
147
+ },
148
+ absolute: {
149
+ type: 'boolean',
150
+ required: false,
151
+ default: DEFAULT_OPTIONS.absolute
152
+ },
153
+ stats: {
154
+ type: 'object',
155
+ required: true,
156
+ minProperties: 1,
157
+ propertyNames: SUPPORTED_STATS,
158
+ propertySchema: {
159
+ type: 'object',
160
+ required: true,
161
+ properties: {
162
+ storeAs: {
163
+ type: 'string',
164
+ required: true,
165
+ minLength: 1,
166
+ validator: validators.identifier,
167
+ error: 'storeAs must be a valid identifier'
168
+ }
169
+ }
170
+ }
171
+ }
172
+ },
173
+
174
+ // Cross-field validators
175
+ crossFieldValidators: [
176
+ {
177
+ fields: [ 'stats', 'timestamp' ],
178
+ validator: ( spec ) => {
179
+ // If slope stat is requested, timestamp field is required
180
+ if ( spec.stats?.slope !== undefined ) {
181
+ return spec.timestamp !== undefined;
182
+ }
183
+ return true;
184
+ },
185
+ error: 'timestamp is required when slope stat is requested'
186
+ }
187
+ ],
188
+
189
+ // Build spec from DSL arguments
190
+ buildSpec: ( name, x, stats, options ) => {
191
+ const spec = {
192
+ nodeType: NODE_TYPE,
193
+ name,
194
+ from: { x },
195
+ stats,
196
+ ...options
197
+ };
198
+ return spec;
199
+ }
200
+ };
201
+
202
+ export const getDSLMetadata = () => ( { ...DSL_METADATA } );