@winkjs/composer 0.0.2 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (451) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +106 -80
  3. package/package.json +56 -28
  4. package/src/composer.js +75 -0
  5. package/src/core/batch/aoo-encoder.js +148 -0
  6. package/src/core/batch/index.js +12 -0
  7. package/src/core/batch/sliding-window.js +91 -0
  8. package/src/core/codec/index.js +137 -0
  9. package/src/core/emitter-manager/mqtt/constants.js +102 -0
  10. package/src/core/emitter-manager/mqtt/emitter.js +780 -0
  11. package/src/core/emitter-manager/mqtt/index.js +190 -0
  12. package/src/core/emitter-manager/mqtt/mqtt-store.js +538 -0
  13. package/src/core/emitter-manager/terminal/assert-columns.js +98 -0
  14. package/src/core/emitter-manager/terminal/formatters.js +160 -0
  15. package/src/core/emitter-manager/terminal/index.js +341 -0
  16. package/src/core/env-vars.js +245 -0
  17. package/src/core/mqtt-protocol.js +47 -0
  18. package/src/core/partition-manager/index.js +7 -0
  19. package/src/core/partition-manager/init.js +44 -0
  20. package/src/core/partition-manager/introspect.js +15 -0
  21. package/src/core/partition-manager/publish-to.js +6 -0
  22. package/src/core/partition-manager/recompute.js +5 -0
  23. package/src/core/partition-manager/reset.js +5 -0
  24. package/src/core/partition-manager/update.js +264 -0
  25. package/src/core/partition-manager/validate-flow.js +157 -0
  26. package/src/core/semantics/digest.js +128 -0
  27. package/src/core/semantics/index.js +83 -0
  28. package/src/core/semantics/loader.js +571 -0
  29. package/src/core/semantics/schemas/asset-class-schema.js +218 -0
  30. package/src/core/semantics/schemas/column-schema.js +651 -0
  31. package/src/core/semantics/schemas/enum-schema.js +153 -0
  32. package/src/core/semantics/schemas/index.js +51 -0
  33. package/src/core/semantics/warnings.js +141 -0
  34. package/src/core/shutdown-manager/index.js +289 -0
  35. package/src/core/source-manager/csv/index.js +171 -0
  36. package/src/core/source-manager/csv/start.js +456 -0
  37. package/src/core/source-manager/mqtt/client.js +406 -0
  38. package/src/core/source-manager/mqtt/constants.js +147 -0
  39. package/src/core/source-manager/mqtt/dedup.js +244 -0
  40. package/src/core/source-manager/mqtt/index.js +260 -0
  41. package/src/core/source-manager/mqtt/status.js +546 -0
  42. package/src/core/source-manager/test-harness/comparator.js +226 -0
  43. package/src/core/source-manager/test-harness/field-generator.js +85 -0
  44. package/src/core/source-manager/test-harness/fuzz.js +87 -0
  45. package/src/core/source-manager/test-harness/index.js +115 -0
  46. package/src/core/source-manager/test-harness/prng.js +57 -0
  47. package/src/core/source-manager/test-harness/start.js +232 -0
  48. package/src/core/source-manager/test-harness/validate.js +213 -0
  49. package/src/core/storage-manager/questdb/assert-columns.js +120 -0
  50. package/src/core/storage-manager/questdb/ensure-tables.js +187 -0
  51. package/src/core/storage-manager/questdb/index.js +1251 -0
  52. package/src/core/storage-manager/questdb/persist-plan.js +488 -0
  53. package/src/core/storage-manager/questdb/writers.js +142 -0
  54. package/src/core/test-utils/sealed-record.js +80 -0
  55. package/src/core/test-utils/soak-signature.js +149 -0
  56. package/src/core/test-utils/tcp-proxy.js +106 -0
  57. package/src/core/tunable/helpers.js +254 -0
  58. package/src/core/tunable/index.js +87 -0
  59. package/src/core/utils/console-formatter/index.js +207 -0
  60. package/src/core/utils/create-smart-store-as/index.js +98 -0
  61. package/src/core/utils/create-smart-store-as/sanitize-field.js +46 -0
  62. package/src/core/utils/create-smart-store-as/smart-truncate.js +93 -0
  63. package/src/core/utils/flow/index.js +5 -0
  64. package/src/core/utils/flow/make-collision-checker.js +23 -0
  65. package/src/core/utils/flow/to-kebab.js +20 -0
  66. package/src/core/utils/half-life/index.js +111 -0
  67. package/src/core/utils/node/disable.js +11 -0
  68. package/src/core/utils/node/enable.js +11 -0
  69. package/src/core/utils/node/execute-triggers.js +40 -0
  70. package/src/core/utils/node/index.js +10 -0
  71. package/src/core/utils/node/pause.js +11 -0
  72. package/src/core/utils/node/populate-predicate-inputs.js +14 -0
  73. package/src/core/utils/node/publish-nan.js +19 -0
  74. package/src/core/utils/node/reset-predicate-inputs.js +11 -0
  75. package/src/core/utils/node/unpause.js +11 -0
  76. package/src/core/utils/node/validate-spec.js +22 -0
  77. package/src/core/utils/options/resolve-field-keyed.js +115 -0
  78. package/src/core/utils/quantize/index.js +75 -0
  79. package/src/core/utils/stats/formulas.js +67 -0
  80. package/src/core/utils/stats/index.js +3 -0
  81. package/src/core/utils/validate/composer-validators.js +141 -0
  82. package/src/core/utils/validate/helpers.js +206 -0
  83. package/src/core/utils/validate/index.js +12 -0
  84. package/src/core/utils/validate/type-validators.js +538 -0
  85. package/src/core/utils/validate/validate.js +107 -0
  86. package/src/core/utils/validate/validators.js +177 -0
  87. package/src/core/wiring/assert-handle.js +57 -0
  88. package/src/core/wiring/assert-module.js +63 -0
  89. package/src/core/wiring/index.js +5 -0
  90. package/src/core/wiring/node-type-to-module.js +17 -0
  91. package/src/core/wiring/wire-emitters.js +256 -0
  92. package/src/core/wiring/wire-linear-graph.js +25 -0
  93. package/src/core/wiring/wire-node.js +157 -0
  94. package/src/core/wiring/wire-semantics.js +173 -0
  95. package/src/core/wiring/wire-storages.js +344 -0
  96. package/src/flow/check-output-collisions.js +93 -0
  97. package/src/flow/consts.js +9 -0
  98. package/src/flow/deep-clone-spec.js +64 -0
  99. package/src/flow/driver.js +171 -0
  100. package/src/flow/expand-single-input-array.js +76 -0
  101. package/src/flow/expand-template-spec.js +93 -0
  102. package/src/flow/flow-api-schemas.js +300 -0
  103. package/src/flow/flow.js +658 -0
  104. package/src/flow/for-each.js +225 -0
  105. package/src/flow/get-signature-pattern.js +31 -0
  106. package/src/flow/get-target-array.js +46 -0
  107. package/src/flow/inspect.js +67 -0
  108. package/src/flow/load-node-module.js +81 -0
  109. package/src/flow/make-node-method.js +112 -0
  110. package/src/flow/resolve-pick-by-field.js +77 -0
  111. package/src/flow/resolve-tunables.js +76 -0
  112. package/src/flow/run.js +392 -0
  113. package/src/flow/serialize.js +161 -0
  114. package/src/flow/signatures-schema.js +222 -0
  115. package/src/flow/spec-builder.js +65 -0
  116. package/src/flow/validate-triggers.js +250 -0
  117. package/src/flow/validate.js +261 -0
  118. package/src/nodes/accumulate/index.js +21 -0
  119. package/src/nodes/accumulate/init.js +48 -0
  120. package/src/nodes/accumulate/introspect.js +123 -0
  121. package/src/nodes/accumulate/publish-to.js +34 -0
  122. package/src/nodes/accumulate/recompute.js +27 -0
  123. package/src/nodes/accumulate/reset.js +25 -0
  124. package/src/nodes/accumulate/update.js +42 -0
  125. package/src/nodes/appraise/calibrate.js +64 -0
  126. package/src/nodes/appraise/decision.js +66 -0
  127. package/src/nodes/appraise/deviation.js +140 -0
  128. package/src/nodes/appraise/index.js +23 -0
  129. package/src/nodes/appraise/init.js +165 -0
  130. package/src/nodes/appraise/integrate.js +45 -0
  131. package/src/nodes/appraise/introspect.js +352 -0
  132. package/src/nodes/appraise/publish-to.js +93 -0
  133. package/src/nodes/appraise/receptor.js +69 -0
  134. package/src/nodes/appraise/recompute.js +79 -0
  135. package/src/nodes/appraise/reset.js +53 -0
  136. package/src/nodes/appraise/update.js +130 -0
  137. package/src/nodes/archive/csv/index.js +6 -0
  138. package/src/nodes/archive/csv/init.js +36 -0
  139. package/src/nodes/archive/csv/load.js +70 -0
  140. package/src/nodes/archive/csv/reset.js +14 -0
  141. package/src/nodes/archive/csv/simple-parser.js +216 -0
  142. package/src/nodes/archive/csv/test.js +173 -0
  143. package/src/nodes/archive/csv/update.js +63 -0
  144. package/src/nodes/butterworth-filter/index.js +18 -0
  145. package/src/nodes/butterworth-filter/init.js +184 -0
  146. package/src/nodes/butterworth-filter/introspect.js +228 -0
  147. package/src/nodes/butterworth-filter/publish-to.js +21 -0
  148. package/src/nodes/butterworth-filter/recompute.js +13 -0
  149. package/src/nodes/butterworth-filter/reset.js +30 -0
  150. package/src/nodes/butterworth-filter/update.js +38 -0
  151. package/src/nodes/categorize/index.js +19 -0
  152. package/src/nodes/categorize/init.js +58 -0
  153. package/src/nodes/categorize/introspect.js +177 -0
  154. package/src/nodes/categorize/publish-to.js +29 -0
  155. package/src/nodes/categorize/recompute.js +14 -0
  156. package/src/nodes/categorize/reset.js +16 -0
  157. package/src/nodes/categorize/update.js +53 -0
  158. package/src/nodes/controller/index.js +9 -0
  159. package/src/nodes/controller/init.js +55 -0
  160. package/src/nodes/controller/introspect.js +91 -0
  161. package/src/nodes/controller/publish-to.js +8 -0
  162. package/src/nodes/controller/recompute.js +9 -0
  163. package/src/nodes/controller/reset.js +17 -0
  164. package/src/nodes/controller/update.js +63 -0
  165. package/src/nodes/diff/index.js +19 -0
  166. package/src/nodes/diff/init.js +37 -0
  167. package/src/nodes/diff/introspect.js +132 -0
  168. package/src/nodes/diff/publish-to.js +21 -0
  169. package/src/nodes/diff/recompute.js +12 -0
  170. package/src/nodes/diff/reset.js +12 -0
  171. package/src/nodes/diff/update.js +37 -0
  172. package/src/nodes/digest-moments/formulas.js +12 -0
  173. package/src/nodes/digest-moments/index.js +14 -0
  174. package/src/nodes/digest-moments/init.js +67 -0
  175. package/src/nodes/digest-moments/introspect.js +154 -0
  176. package/src/nodes/digest-moments/publish-to.js +34 -0
  177. package/src/nodes/digest-moments/recompute.js +14 -0
  178. package/src/nodes/digest-moments/reset.js +14 -0
  179. package/src/nodes/digest-moments/update.js +120 -0
  180. package/src/nodes/dwell-time-tracker/index.js +14 -0
  181. package/src/nodes/dwell-time-tracker/init.js +59 -0
  182. package/src/nodes/dwell-time-tracker/introspect.js +137 -0
  183. package/src/nodes/dwell-time-tracker/publish-to.js +22 -0
  184. package/src/nodes/dwell-time-tracker/recompute.js +8 -0
  185. package/src/nodes/dwell-time-tracker/reset.js +23 -0
  186. package/src/nodes/dwell-time-tracker/update.js +103 -0
  187. package/src/nodes/emit-if/helpers.js +94 -0
  188. package/src/nodes/emit-if/index.js +16 -0
  189. package/src/nodes/emit-if/init.js +67 -0
  190. package/src/nodes/emit-if/introspect.js +102 -0
  191. package/src/nodes/emit-if/publish-to.js +13 -0
  192. package/src/nodes/emit-if/recompute.js +12 -0
  193. package/src/nodes/emit-if/reset.js +27 -0
  194. package/src/nodes/emit-if/update.js +93 -0
  195. package/src/nodes/es-correlation/index.js +19 -0
  196. package/src/nodes/es-correlation/init.js +73 -0
  197. package/src/nodes/es-correlation/introspect.js +176 -0
  198. package/src/nodes/es-correlation/publish-to.js +32 -0
  199. package/src/nodes/es-correlation/recompute.js +56 -0
  200. package/src/nodes/es-correlation/reset.js +27 -0
  201. package/src/nodes/es-correlation/update.js +110 -0
  202. package/src/nodes/es-mean/index.js +20 -0
  203. package/src/nodes/es-mean/init.js +58 -0
  204. package/src/nodes/es-mean/introspect.js +129 -0
  205. package/src/nodes/es-mean/publish-to.js +19 -0
  206. package/src/nodes/es-mean/recompute.js +16 -0
  207. package/src/nodes/es-mean/reset.js +24 -0
  208. package/src/nodes/es-mean/update.js +69 -0
  209. package/src/nodes/es-pairwise-correlation/index.js +19 -0
  210. package/src/nodes/es-pairwise-correlation/init.js +98 -0
  211. package/src/nodes/es-pairwise-correlation/introspect.js +187 -0
  212. package/src/nodes/es-pairwise-correlation/publish-to.js +31 -0
  213. package/src/nodes/es-pairwise-correlation/recompute.js +69 -0
  214. package/src/nodes/es-pairwise-correlation/reset.js +29 -0
  215. package/src/nodes/es-pairwise-correlation/update.js +89 -0
  216. package/src/nodes/es-stats/index.js +21 -0
  217. package/src/nodes/es-stats/init.js +105 -0
  218. package/src/nodes/es-stats/introspect.js +157 -0
  219. package/src/nodes/es-stats/publish-to.js +42 -0
  220. package/src/nodes/es-stats/recompute.js +74 -0
  221. package/src/nodes/es-stats/reset.js +49 -0
  222. package/src/nodes/es-stats/update-envelope.js +44 -0
  223. package/src/nodes/es-stats/update-welford.js +64 -0
  224. package/src/nodes/es-stats/update.js +59 -0
  225. package/src/nodes/index.js +83 -0
  226. package/src/nodes/invert-flag/index.js +20 -0
  227. package/src/nodes/invert-flag/init.js +36 -0
  228. package/src/nodes/invert-flag/introspect.js +115 -0
  229. package/src/nodes/invert-flag/publish-to.js +23 -0
  230. package/src/nodes/invert-flag/recompute.js +12 -0
  231. package/src/nodes/invert-flag/reset.js +12 -0
  232. package/src/nodes/invert-flag/update.js +32 -0
  233. package/src/nodes/kalman1d/index.js +20 -0
  234. package/src/nodes/kalman1d/init.js +103 -0
  235. package/src/nodes/kalman1d/introspect.js +250 -0
  236. package/src/nodes/kalman1d/publish-to.js +34 -0
  237. package/src/nodes/kalman1d/recompute.js +23 -0
  238. package/src/nodes/kalman1d/reset.js +29 -0
  239. package/src/nodes/kalman1d/update.js +144 -0
  240. package/src/nodes/kernel/index.js +18 -0
  241. package/src/nodes/kernel/init.js +66 -0
  242. package/src/nodes/kernel/introspect.js +163 -0
  243. package/src/nodes/kernel/presets.js +74 -0
  244. package/src/nodes/kernel/publish-to.js +25 -0
  245. package/src/nodes/kernel/recompute.js +14 -0
  246. package/src/nodes/kernel/reset.js +20 -0
  247. package/src/nodes/kernel/update.js +69 -0
  248. package/src/nodes/lag/index.js +21 -0
  249. package/src/nodes/lag/init.js +87 -0
  250. package/src/nodes/lag/introspect.js +202 -0
  251. package/src/nodes/lag/publish-to.js +64 -0
  252. package/src/nodes/lag/recompute.js +33 -0
  253. package/src/nodes/lag/reset.js +49 -0
  254. package/src/nodes/lag/update.js +133 -0
  255. package/src/nodes/median3/index.js +19 -0
  256. package/src/nodes/median3/init.js +40 -0
  257. package/src/nodes/median3/introspect.js +116 -0
  258. package/src/nodes/median3/publish-to.js +21 -0
  259. package/src/nodes/median3/recompute.js +12 -0
  260. package/src/nodes/median3/reset.js +15 -0
  261. package/src/nodes/median3/update.js +58 -0
  262. package/src/nodes/moments-digest/copy-from.js +12 -0
  263. package/src/nodes/moments-digest/flush.js +17 -0
  264. package/src/nodes/moments-digest/index.js +132 -0
  265. package/src/nodes/moments-digest/init.js +84 -0
  266. package/src/nodes/moments-digest/introspect.js +122 -0
  267. package/src/nodes/moments-digest/prelude.js +28 -0
  268. package/src/nodes/moments-digest/publish-to.js +31 -0
  269. package/src/nodes/moments-digest/recompute.js +9 -0
  270. package/src/nodes/moments-digest/reset.js +20 -0
  271. package/src/nodes/moments-digest/update.js +182 -0
  272. package/src/nodes/node-names.js +67 -0
  273. package/src/nodes/page-hinkley/index.js +20 -0
  274. package/src/nodes/page-hinkley/init.js +66 -0
  275. package/src/nodes/page-hinkley/introspect.js +193 -0
  276. package/src/nodes/page-hinkley/publish-to.js +28 -0
  277. package/src/nodes/page-hinkley/recompute.js +14 -0
  278. package/src/nodes/page-hinkley/reset.js +23 -0
  279. package/src/nodes/page-hinkley/update.js +105 -0
  280. package/src/nodes/pass-if/index.js +21 -0
  281. package/src/nodes/pass-if/init.js +37 -0
  282. package/src/nodes/pass-if/introspect.js +93 -0
  283. package/src/nodes/pass-if/publish-to.js +15 -0
  284. package/src/nodes/pass-if/recompute.js +15 -0
  285. package/src/nodes/pass-if/reset.js +18 -0
  286. package/src/nodes/pass-if/update.js +37 -0
  287. package/src/nodes/persist-if/helpers.js +111 -0
  288. package/src/nodes/persist-if/index.js +9 -0
  289. package/src/nodes/persist-if/init.js +98 -0
  290. package/src/nodes/persist-if/introspect.js +106 -0
  291. package/src/nodes/persist-if/publish-to.js +21 -0
  292. package/src/nodes/persist-if/recompute.js +20 -0
  293. package/src/nodes/persist-if/reset.js +35 -0
  294. package/src/nodes/persist-if/update.js +112 -0
  295. package/src/nodes/persistence-check/index.js +14 -0
  296. package/src/nodes/persistence-check/init.js +38 -0
  297. package/src/nodes/persistence-check/introspect.js +144 -0
  298. package/src/nodes/persistence-check/publish-to.js +11 -0
  299. package/src/nodes/persistence-check/recompute.js +7 -0
  300. package/src/nodes/persistence-check/reset.js +12 -0
  301. package/src/nodes/persistence-check/update.js +57 -0
  302. package/src/nodes/process-index/index.js +15 -0
  303. package/src/nodes/process-index/init.js +48 -0
  304. package/src/nodes/process-index/introspect.js +182 -0
  305. package/src/nodes/process-index/publish-to.js +29 -0
  306. package/src/nodes/process-index/recompute.js +8 -0
  307. package/src/nodes/process-index/reset.js +8 -0
  308. package/src/nodes/process-index/update.js +75 -0
  309. package/src/nodes/ratio/index.js +14 -0
  310. package/src/nodes/ratio/init.js +37 -0
  311. package/src/nodes/ratio/introspect.js +155 -0
  312. package/src/nodes/ratio/publish-to.js +15 -0
  313. package/src/nodes/ratio/recompute.js +7 -0
  314. package/src/nodes/ratio/reset.js +7 -0
  315. package/src/nodes/ratio/update.js +47 -0
  316. package/src/nodes/sanitize/index.js +82 -0
  317. package/src/nodes/sanitize/init.js +82 -0
  318. package/src/nodes/sanitize/introspect.js +194 -0
  319. package/src/nodes/sanitize/publish-to.js +15 -0
  320. package/src/nodes/sanitize/recompute.js +8 -0
  321. package/src/nodes/sanitize/reset.js +14 -0
  322. package/src/nodes/sanitize/update.js +102 -0
  323. package/src/nodes/spike-guard/index.js +14 -0
  324. package/src/nodes/spike-guard/init.js +47 -0
  325. package/src/nodes/spike-guard/introspect.js +130 -0
  326. package/src/nodes/spike-guard/publish-to.js +35 -0
  327. package/src/nodes/spike-guard/recompute.js +14 -0
  328. package/src/nodes/spike-guard/reset.js +19 -0
  329. package/src/nodes/spike-guard/update.js +89 -0
  330. package/src/nodes/sse-emitter/index.js +7 -0
  331. package/src/nodes/sse-emitter/init.js +105 -0
  332. package/src/nodes/sse-emitter/introspect.js +134 -0
  333. package/src/nodes/sse-emitter/publish-to.js +6 -0
  334. package/src/nodes/sse-emitter/recompute.js +16 -0
  335. package/src/nodes/sse-emitter/reset.js +19 -0
  336. package/src/nodes/sse-emitter/update.js +69 -0
  337. package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +36 -0
  338. package/src/nodes/sse-emitter/utils/build-message.js +31 -0
  339. package/src/nodes/sse-emitter/utils/connection-manager.js +45 -0
  340. package/src/nodes/sse-emitter/utils/endpoint-initializer.js +20 -0
  341. package/src/nodes/sse-emitter/utils/health-monitor.js +38 -0
  342. package/src/nodes/sse-emitter/utils/qos-handler.js +41 -0
  343. package/src/nodes/state-change-detector/helpers.js +35 -0
  344. package/src/nodes/state-change-detector/index.js +12 -0
  345. package/src/nodes/state-change-detector/init.js +56 -0
  346. package/src/nodes/state-change-detector/introspect.js +146 -0
  347. package/src/nodes/state-change-detector/publish-to.js +20 -0
  348. package/src/nodes/state-change-detector/recompute.js +6 -0
  349. package/src/nodes/state-change-detector/reset.js +20 -0
  350. package/src/nodes/state-change-detector/update.js +97 -0
  351. package/src/nodes/sw-stats/index.js +14 -0
  352. package/src/nodes/sw-stats/init.js +51 -0
  353. package/src/nodes/sw-stats/introspect.js +131 -0
  354. package/src/nodes/sw-stats/publish-to.js +45 -0
  355. package/src/nodes/sw-stats/recompute.js +33 -0
  356. package/src/nodes/sw-stats/reset.js +15 -0
  357. package/src/nodes/sw-stats/update.js +39 -0
  358. package/src/nodes/swing-watch/index.js +17 -0
  359. package/src/nodes/swing-watch/init.js +144 -0
  360. package/src/nodes/swing-watch/introspect.js +188 -0
  361. package/src/nodes/swing-watch/publish-to.js +53 -0
  362. package/src/nodes/swing-watch/recompute.js +22 -0
  363. package/src/nodes/swing-watch/reset.js +55 -0
  364. package/src/nodes/swing-watch/update.js +767 -0
  365. package/src/nodes/tally/index.js +15 -0
  366. package/src/nodes/tally/init.js +32 -0
  367. package/src/nodes/tally/introspect.js +116 -0
  368. package/src/nodes/tally/publish-to.js +28 -0
  369. package/src/nodes/tally/recompute.js +10 -0
  370. package/src/nodes/tally/reset.js +10 -0
  371. package/src/nodes/tally/update.js +44 -0
  372. package/src/nodes/threshold/index.js +14 -0
  373. package/src/nodes/threshold/init.js +121 -0
  374. package/src/nodes/threshold/introspect.js +192 -0
  375. package/src/nodes/threshold/publish-to.js +11 -0
  376. package/src/nodes/threshold/recompute.js +8 -0
  377. package/src/nodes/threshold/reset.js +14 -0
  378. package/src/nodes/threshold/update.js +54 -0
  379. package/src/nodes/transform/helpers.js +39 -0
  380. package/src/nodes/transform/index.js +15 -0
  381. package/src/nodes/transform/init.js +35 -0
  382. package/src/nodes/transform/introspect.js +118 -0
  383. package/src/nodes/transform/publish-to.js +23 -0
  384. package/src/nodes/transform/recompute.js +12 -0
  385. package/src/nodes/transform/reset.js +11 -0
  386. package/src/nodes/transform/update.js +32 -0
  387. package/src/nodes/trend/compute-confidence.js +112 -0
  388. package/src/nodes/trend/index.js +14 -0
  389. package/src/nodes/trend/init.js +81 -0
  390. package/src/nodes/trend/introspect.js +151 -0
  391. package/src/nodes/trend/publish-to.js +22 -0
  392. package/src/nodes/trend/recompute.js +15 -0
  393. package/src/nodes/trend/reset.js +26 -0
  394. package/src/nodes/trend/update.js +93 -0
  395. package/src/nodes/tw-stats/copy-from.js +16 -0
  396. package/src/nodes/tw-stats/flush.js +12 -0
  397. package/src/nodes/tw-stats/index.js +41 -0
  398. package/src/nodes/tw-stats/init.js +92 -0
  399. package/src/nodes/tw-stats/introspect.js +171 -0
  400. package/src/nodes/tw-stats/publish-to.js +108 -0
  401. package/src/nodes/tw-stats/recompute.js +9 -0
  402. package/src/nodes/tw-stats/reset.js +16 -0
  403. package/src/nodes/tw-stats/update.js +113 -0
  404. package/src/nodes/unbalance/index.js +15 -0
  405. package/src/nodes/unbalance/init.js +59 -0
  406. package/src/nodes/unbalance/introspect.js +167 -0
  407. package/src/nodes/unbalance/publish-to.js +52 -0
  408. package/src/nodes/unbalance/recompute.js +11 -0
  409. package/src/nodes/unbalance/reset.js +10 -0
  410. package/src/nodes/unbalance/update.js +83 -0
  411. package/src/nodes/vector-distance/index.js +14 -0
  412. package/src/nodes/vector-distance/init.js +54 -0
  413. package/src/nodes/vector-distance/introspect.js +149 -0
  414. package/src/nodes/vector-distance/publish-to.js +35 -0
  415. package/src/nodes/vector-distance/recompute.js +11 -0
  416. package/src/nodes/vector-distance/reset.js +20 -0
  417. package/src/nodes/vector-distance/update.js +139 -0
  418. package/src/nodes/winnow/index.js +12 -0
  419. package/src/nodes/winnow/init.js +82 -0
  420. package/src/nodes/winnow/introspect.js +253 -0
  421. package/src/nodes/winnow/publish-to.js +51 -0
  422. package/src/nodes/winnow/recompute.js +14 -0
  423. package/src/nodes/winnow/reset.js +34 -0
  424. package/src/nodes/winnow/update.js +203 -0
  425. package/src/tools/training/features.js +227 -0
  426. package/src/tools/training/index.js +21 -0
  427. package/src/tools/training/logistic.js +238 -0
  428. package/src/tools/training/metrics.js +126 -0
  429. package/src/tools/training/scale.js +239 -0
  430. package/src/tools/training/validate.js +103 -0
  431. package/src/windowing/count-sliding/create.js +16 -0
  432. package/src/windowing/count-sliding/get-reverse-chronological-index.js +19 -0
  433. package/src/windowing/count-sliding/index.js +6 -0
  434. package/src/windowing/count-sliding/is-not-full.js +5 -0
  435. package/src/windowing/count-sliding/push.js +15 -0
  436. package/src/windowing/count-sliding/reset.js +11 -0
  437. package/src/windowing/count-sliding/size.js +5 -0
  438. package/src/windowing/time-sliding/create.js +25 -0
  439. package/src/windowing/time-sliding/push.js +42 -0
  440. package/CODE_OF_CONDUCT.md +0 -46
  441. package/CONTRIBUTING.md +0 -132
  442. package/docs/.nojekyll +0 -0
  443. package/docs/CONTRIBUTING.md +0 -134
  444. package/docs/README.md +0 -132
  445. package/docs/_coverpage.md +0 -26
  446. package/docs/_media/bg.png +0 -0
  447. package/docs/_media/wink-logo.png +0 -0
  448. package/docs/_navbar.md +0 -14
  449. package/docs/_sidebar.md +0 -2
  450. package/docs/index.html +0 -44
  451. package/eslint.config.js +0 -283
@@ -0,0 +1,651 @@
1
+ // core/semantics/schemas/column-schema.js
2
+
3
+ /**
4
+ * @fileoverview Column Schema Definition
5
+ *
6
+ * Defines the validation schema for individual columns within an asset class.
7
+ * Columns represent measurements or computed values with full semantic context.
8
+ *
9
+ * Key design decisions:
10
+ * - Three-tier limit hierarchy: physicalRange → operational → specification
11
+ * - Intuitive naming: criticalLow/High, warningLow/High (not hiHi/loLo)
12
+ * - Meaningful defaults: hysteresis=0, interpretation='informational'
13
+ * - Context-dependent limits via `contexts` array
14
+ *
15
+ * Context Resolution Semantics:
16
+ * - Contexts are evaluated in array order (first match wins)
17
+ * - Each context's `when` clause is tested against current column values
18
+ * - At most one `when: 'default'` context is allowed per column
19
+ * - Default context serves as fallback when no other context matches
20
+ * - Recommended ordering: specific conditions first, default last
21
+ *
22
+ * Example context ordering:
23
+ * contexts: [
24
+ * { when: { column: 'state', equals: 2 }, ... }, // Error state
25
+ * { when: { column: 'state', equals: 1 }, ... }, // Running
26
+ * { when: 'default', ... } // Fallback (Idle, etc.)
27
+ * ]
28
+ *
29
+ * Hysteresis Precedence (intended resolution order for consumers):
30
+ * operational.hysteresis → column.hysteresis → COLUMN_DEFAULTS.hysteresis (0)
31
+ */
32
+
33
+ import { validators } from '../../utils/validate/index.js';
34
+
35
+ // ============================================================================
36
+ // CONSTANTS
37
+ // ============================================================================
38
+
39
+ /**
40
+ * Valid data types for columns.
41
+ * @type {string[]}
42
+ */
43
+ const COLUMN_TYPES = [ 'float64', 'int64', 'string', 'bool', 'timestamp' ];
44
+
45
+ /**
46
+ * LLM-native interpretation array.
47
+ *
48
+ * Column-level `interpretation` is an array of natural language strings
49
+ * providing guidance to LLM consumers about how to interpret values.
50
+ *
51
+ * Recommended prefixes (convention, not validated):
52
+ * Severity: - How to interpret magnitude
53
+ * Threshold: - Key boundaries and meanings
54
+ * Correlate: - Related tables/metrics
55
+ * Trend: - Temporal pattern significance
56
+ * Pattern: - Multi-signal signatures
57
+ * Action: - Recommended response
58
+ * Context: - Domain background
59
+ *
60
+ * Example:
61
+ * interpretation: [
62
+ * "Severity: higher values indicate more severe glitches",
63
+ * "Threshold: > 30 bar indicates communication dropout",
64
+ * "Correlate: with washCycleStats by timestamp"
65
+ * ]
66
+ *
67
+ * @see ADR-001 (interpretation arrays)
68
+ */
69
+
70
+ /**
71
+ * Default values for optional column properties.
72
+ * @type {Object}
73
+ */
74
+ const COLUMN_DEFAULTS = {
75
+ unit: '',
76
+ resolution: 1,
77
+ description: '',
78
+ interpretation: [],
79
+ hysteresis: 0
80
+ };
81
+
82
+ /**
83
+ * Allowed properties for 'when' clause (object form).
84
+ * Used for unknown property detection in validWhen.
85
+ * Note: 'when' has union type (string | object), so we can't use
86
+ * schema-based propertyNames directly.
87
+ * @type {Set<string>}
88
+ */
89
+ const WHEN_ALLOWED_KEYS = new Set( [ 'column', 'equals', 'oneOf' ] );
90
+
91
+ // ============================================================================
92
+ // CUSTOM VALIDATORS
93
+ // ============================================================================
94
+
95
+ /**
96
+ * Validates physical range object.
97
+ *
98
+ * @param {Object} range - Physical range with min/max
99
+ * @returns {boolean} True if valid
100
+ */
101
+ const validPhysicalRange = function ( range ) {
102
+ if ( typeof range !== 'object' || range === null ) {
103
+ return false;
104
+ }
105
+ if ( typeof range.min !== 'number' || typeof range.max !== 'number' ) {
106
+ return false;
107
+ }
108
+ if ( !Number.isFinite( range.min ) || !Number.isFinite( range.max ) ) {
109
+ return false;
110
+ }
111
+ return range.min < range.max;
112
+ };
113
+
114
+ /**
115
+ * Validates operational limits object.
116
+ * Limits must be in ascending order: criticalLow ≤ warningLow ≤ target ≤ warningHigh ≤ criticalHigh
117
+ *
118
+ * @param {Object} ops - Operational limits
119
+ * @returns {boolean} True if valid
120
+ */
121
+ const validOperational = function ( ops ) {
122
+ if ( typeof ops !== 'object' || ops === null ) {
123
+ return false;
124
+ }
125
+
126
+ // Validate individual fields if present
127
+ const numericFields = [ 'criticalLow', 'warningLow', 'target', 'warningHigh', 'criticalHigh' ];
128
+ for ( let i = 0; i < numericFields.length; i += 1 ) {
129
+ const field = numericFields[ i ];
130
+ if ( ops[ field ] !== undefined ) {
131
+ if ( typeof ops[ field ] !== 'number' || !Number.isFinite( ops[ field ] ) ) {
132
+ return false;
133
+ }
134
+ }
135
+ }
136
+
137
+ // Validate hysteresis if present (must be non-negative finite)
138
+ if ( ops.hysteresis !== undefined ) {
139
+ if ( !Number.isFinite( ops.hysteresis ) || ops.hysteresis < 0 ) {
140
+ return false;
141
+ }
142
+ }
143
+
144
+ // Validate ascending order of defined limits
145
+ const limits = [];
146
+ if ( ops.criticalLow !== undefined ) limits.push( ops.criticalLow );
147
+ if ( ops.warningLow !== undefined ) limits.push( ops.warningLow );
148
+ if ( ops.target !== undefined ) limits.push( ops.target );
149
+ if ( ops.warningHigh !== undefined ) limits.push( ops.warningHigh );
150
+ if ( ops.criticalHigh !== undefined ) limits.push( ops.criticalHigh );
151
+
152
+ for ( let i = 1; i < limits.length; i += 1 ) {
153
+ if ( limits[ i ] < limits[ i - 1 ] ) {
154
+ return false;
155
+ }
156
+ }
157
+
158
+ return true;
159
+ };
160
+
161
+ /**
162
+ * Validates specification limits object.
163
+ *
164
+ * @param {Object} spec - Specification limits with lsl/usl/target
165
+ * @returns {boolean} True if valid
166
+ */
167
+ const validSpecification = function ( spec ) {
168
+ if ( typeof spec !== 'object' || spec === null ) {
169
+ return false;
170
+ }
171
+
172
+ // Validate individual fields if present
173
+ const fields = [ 'lowerSpecLimit', 'upperSpecLimit', 'target' ];
174
+ for ( let i = 0; i < fields.length; i += 1 ) {
175
+ const field = fields[ i ];
176
+ if ( spec[ field ] !== undefined ) {
177
+ if ( typeof spec[ field ] !== 'number' || !Number.isFinite( spec[ field ] ) ) {
178
+ return false;
179
+ }
180
+ }
181
+ }
182
+
183
+ // lowerSpecLimit must be less than upperSpecLimit
184
+ if ( spec.lowerSpecLimit !== undefined && spec.upperSpecLimit !== undefined ) {
185
+ if ( spec.lowerSpecLimit >= spec.upperSpecLimit ) {
186
+ return false;
187
+ }
188
+ }
189
+
190
+ // target must be between lowerSpecLimit and upperSpecLimit
191
+ if ( spec.target !== undefined ) {
192
+ if ( spec.lowerSpecLimit !== undefined && spec.target < spec.lowerSpecLimit ) {
193
+ return false;
194
+ }
195
+ if ( spec.upperSpecLimit !== undefined && spec.target > spec.upperSpecLimit ) {
196
+ return false;
197
+ }
198
+ }
199
+
200
+ return true;
201
+ };
202
+
203
+ /**
204
+ * Checks if a value is a JSON primitive (number, string, boolean, null).
205
+ *
206
+ * @param {*} value - Value to check
207
+ * @returns {boolean} True if primitive
208
+ */
209
+ const isPrimitive = function ( value ) {
210
+ const type = typeof value;
211
+ return value === null || type === 'number' || type === 'string' || type === 'boolean';
212
+ };
213
+
214
+ /**
215
+ * Validates a 'when' clause in a context entry.
216
+ * Supports:
217
+ * - "default" - fallback when no other context matches
218
+ * - { column, equals } - exact value match (any primitive)
219
+ * - { column, oneOf } - value in set (array of primitives)
220
+ * Extensible for future operators (greaterThan, between, etc.)
221
+ *
222
+ * @param {string|Object} when - When clause
223
+ * @returns {boolean} True if valid
224
+ */
225
+ const validWhen = function ( when ) {
226
+ // "default" string for fallback
227
+ if ( when === 'default' ) {
228
+ return true;
229
+ }
230
+
231
+ // Object form with column and operator
232
+ if ( typeof when !== 'object' || when === null ) {
233
+ return false;
234
+ }
235
+
236
+ // Check for unknown properties
237
+ const keys = Object.keys( when );
238
+ for ( let i = 0; i < keys.length; i += 1 ) {
239
+ if ( !WHEN_ALLOWED_KEYS.has( keys[ i ] ) ) {
240
+ return false;
241
+ }
242
+ }
243
+
244
+ // Must have column name
245
+ if ( typeof when.column !== 'string' || when.column.length === 0 ) {
246
+ return false;
247
+ }
248
+
249
+ // Check operators (mutually exclusive)
250
+ const hasEquals = when.equals !== undefined;
251
+ const hasOneOf = when.oneOf !== undefined;
252
+
253
+ // Exactly one operator must be present (extensible for future operators)
254
+ const operatorCount = ( hasEquals ? 1 : 0 ) + ( hasOneOf ? 1 : 0 );
255
+ if ( operatorCount !== 1 ) {
256
+ return false;
257
+ }
258
+
259
+ // Validate equals: must be a primitive
260
+ if ( hasEquals && !isPrimitive( when.equals ) ) {
261
+ return false;
262
+ }
263
+
264
+ // Validate oneOf: must be non-empty array of primitives
265
+ if ( hasOneOf ) {
266
+ if ( !Array.isArray( when.oneOf ) || when.oneOf.length === 0 ) {
267
+ return false;
268
+ }
269
+ for ( let i = 0; i < when.oneOf.length; i += 1 ) {
270
+ if ( !isPrimitive( when.oneOf[ i ] ) ) {
271
+ return false;
272
+ }
273
+ }
274
+ }
275
+
276
+ return true;
277
+ };
278
+
279
+ /**
280
+ * Validates column type.
281
+ *
282
+ * @param {string} type - Column type
283
+ * @returns {boolean} True if valid
284
+ */
285
+ const validColumnType = function ( type ) {
286
+ return COLUMN_TYPES.includes( type );
287
+ };
288
+
289
+ /**
290
+ * Validates interpretation array.
291
+ * Must be an array of non-empty strings.
292
+ *
293
+ * @param {Array} interps - Interpretation array
294
+ * @returns {boolean} True if valid
295
+ */
296
+ const validInterpretation = function ( interps ) {
297
+ if ( !Array.isArray( interps ) ) {
298
+ return false;
299
+ }
300
+ for ( let i = 0; i < interps.length; i += 1 ) {
301
+ if ( typeof interps[ i ] !== 'string' || interps[ i ].length === 0 ) {
302
+ return false;
303
+ }
304
+ }
305
+ return true;
306
+ };
307
+
308
+ /**
309
+ * Validates a single context entry.
310
+ * Each context must have:
311
+ * - A valid 'when' clause
312
+ * - At least one of 'operational', 'specification', or 'interpretation'
313
+ *
314
+ * Optional context-specific interpretation allows different "what's bad"
315
+ * semantics per operating mode (e.g., pressure during idle vs operation).
316
+ *
317
+ * @param {Object} ctx - Context entry
318
+ * @returns {boolean} True if valid
319
+ */
320
+ const validContext = function ( ctx ) {
321
+ if ( typeof ctx !== 'object' || ctx === null ) {
322
+ return false;
323
+ }
324
+
325
+ // Validate 'when' clause
326
+ if ( !validWhen( ctx.when ) ) {
327
+ return false;
328
+ }
329
+
330
+ // Check what's present
331
+ const hasOperational = ctx.operational !== undefined;
332
+ const hasSpecification = ctx.specification !== undefined;
333
+ const hasInterpretation = ctx.interpretation !== undefined;
334
+
335
+ // Must have at least one of operational, specification, or interpretation
336
+ if ( !hasOperational && !hasSpecification && !hasInterpretation ) {
337
+ return false;
338
+ }
339
+
340
+ // Validate operational if present
341
+ if ( hasOperational && !validOperational( ctx.operational ) ) {
342
+ return false;
343
+ }
344
+
345
+ // Validate specification if present
346
+ if ( hasSpecification && !validSpecification( ctx.specification ) ) {
347
+ return false;
348
+ }
349
+
350
+ // Validate interpretation if present
351
+ if ( hasInterpretation && !validInterpretation( ctx.interpretation ) ) {
352
+ return false;
353
+ }
354
+
355
+ return true;
356
+ };
357
+
358
+ /**
359
+ * Validates contexts array.
360
+ * Ensures each context is valid and at most one default context exists.
361
+ *
362
+ * @param {Array} contexts - Array of context entries
363
+ * @returns {boolean} True if valid
364
+ */
365
+ const validContexts = function ( contexts ) {
366
+ if ( !Array.isArray( contexts ) ) {
367
+ return false;
368
+ }
369
+
370
+ let defaultCount = 0;
371
+ for ( let i = 0; i < contexts.length; i += 1 ) {
372
+ if ( !validContext( contexts[ i ] ) ) {
373
+ return false;
374
+ }
375
+ // Count default contexts
376
+ if ( contexts[ i ].when === 'default' ) {
377
+ defaultCount += 1;
378
+ }
379
+ }
380
+
381
+ // At most one default context allowed
382
+ if ( defaultCount > 1 ) {
383
+ return false;
384
+ }
385
+
386
+ return true;
387
+ };
388
+
389
+ // ============================================================================
390
+ // SCHEMA DEFINITION
391
+ // ============================================================================
392
+
393
+ /**
394
+ * Cross-field validator for mutual exclusivity of contexts vs direct limits.
395
+ * Rule: If contexts exists, operational/specification must NOT exist at column level.
396
+ * If operational/specification exists at column level, contexts must NOT exist.
397
+ *
398
+ * @param {Object} column - Column definition
399
+ * @returns {boolean} True if valid
400
+ */
401
+ const validLimitsMutualExclusivity = function ( column ) {
402
+ const hasContexts = column.contexts !== undefined && column.contexts.length > 0;
403
+ const hasDirectOperational = column.operational !== undefined;
404
+ const hasDirectSpecification = column.specification !== undefined;
405
+
406
+ // Mutual exclusivity: contexts XOR direct limits
407
+ if ( hasContexts && ( hasDirectOperational || hasDirectSpecification ) ) {
408
+ return false;
409
+ }
410
+
411
+ return true;
412
+ };
413
+
414
+ // Field names for operational and specification limit checks
415
+ const OPS_LIMIT_FIELDS = [ 'criticalLow', 'warningLow', 'target', 'warningHigh', 'criticalHigh' ];
416
+ const SPEC_LIMIT_FIELDS = [ 'lowerSpecLimit', 'target', 'upperSpecLimit' ];
417
+
418
+ /**
419
+ * Checks if all defined fields in object are within [min, max] range.
420
+ *
421
+ * @param {Object} obj - Object containing limit fields
422
+ * @param {string[]} fields - Field names to check
423
+ * @param {number} min - Minimum allowed value
424
+ * @param {number} max - Maximum allowed value
425
+ * @returns {boolean} True if all defined fields are within range
426
+ */
427
+ const allFieldsWithinRange = function ( obj, fields, min, max ) {
428
+ for ( let i = 0; i < fields.length; i += 1 ) {
429
+ const val = obj[ fields[ i ] ];
430
+ if ( val !== undefined && ( val < min || val > max ) ) {
431
+ return false;
432
+ }
433
+ }
434
+ return true;
435
+ }; // allFieldsWithinRange()
436
+
437
+ /**
438
+ * Checks if specification limits are within operational critical bounds.
439
+ *
440
+ * @param {Object} ops - Operational limits with criticalLow/criticalHigh
441
+ * @param {Object} spec - Specification with lowerSpecLimit/upperSpecLimit
442
+ * @returns {boolean} True if spec is within ops bounds
443
+ */
444
+ const specWithinOperational = function ( ops, spec ) {
445
+ // lowerSpecLimit must be >= criticalLow (if both defined)
446
+ if ( spec.lowerSpecLimit !== undefined && ops.criticalLow !== undefined ) {
447
+ if ( spec.lowerSpecLimit < ops.criticalLow ) return false;
448
+ }
449
+ // upperSpecLimit must be <= criticalHigh (if both defined)
450
+ if ( spec.upperSpecLimit !== undefined && ops.criticalHigh !== undefined ) {
451
+ if ( spec.upperSpecLimit > ops.criticalHigh ) return false;
452
+ }
453
+ return true;
454
+ }; // specWithinOperational()
455
+
456
+ /**
457
+ * Validates limits hierarchy: physicalRange ⊇ operational ⊇ specification.
458
+ *
459
+ * Rules:
460
+ * 1. If physicalRange exists, operational limits must be within [min, max]
461
+ * 2. If physicalRange exists, specification limits must be within [min, max]
462
+ * 3. If operational + specification both exist (with or without physicalRange):
463
+ * - specification.lsl >= operational.criticalLow (if both defined)
464
+ * - specification.usl <= operational.criticalHigh (if both defined)
465
+ *
466
+ * @param {Object} column - Column definition
467
+ * @returns {boolean} True if hierarchy is valid
468
+ */
469
+ const validLimitsHierarchy = function ( column ) {
470
+ const range = column.physicalRange;
471
+ const ops = column.operational;
472
+ const spec = column.specification;
473
+
474
+ // Rule 1: Check operational within physicalRange
475
+ if ( range && ops ) {
476
+ if ( !allFieldsWithinRange( ops, OPS_LIMIT_FIELDS, range.min, range.max ) ) {
477
+ return false;
478
+ }
479
+ }
480
+
481
+ // Rule 2: Check specification within physicalRange
482
+ if ( range && spec ) {
483
+ if ( !allFieldsWithinRange( spec, SPEC_LIMIT_FIELDS, range.min, range.max ) ) {
484
+ return false;
485
+ }
486
+ }
487
+
488
+ // Rule 3: Check specification within operational bounds
489
+ if ( ops && spec ) {
490
+ if ( !specWithinOperational( ops, spec ) ) return false;
491
+ }
492
+
493
+ return true;
494
+ }; // validLimitsHierarchy()
495
+
496
+ /**
497
+ * Validates context limits hierarchy: physicalRange ⊇ ctx.operational ⊇ ctx.specification.
498
+ *
499
+ * Rules (applied to each context):
500
+ * 1. If column has physicalRange, context operational must be within [min, max]
501
+ * 2. If column has physicalRange, context specification must be within [min, max]
502
+ * 3. If context has both operational + specification:
503
+ * - specification.lsl >= operational.criticalLow (if both defined)
504
+ * - specification.usl <= operational.criticalHigh (if both defined)
505
+ *
506
+ * @param {Object} column - Column definition with contexts array
507
+ * @returns {boolean} True if all context hierarchies are valid
508
+ */
509
+ const validContextLimitsHierarchy = function ( column ) {
510
+ const range = column.physicalRange;
511
+ const contexts = column.contexts;
512
+
513
+ // No contexts or empty contexts = valid
514
+ if ( !contexts || contexts.length === 0 ) return true;
515
+
516
+ for ( let i = 0; i < contexts.length; i += 1 ) {
517
+ const ctx = contexts[ i ];
518
+ const ops = ctx.operational;
519
+ const spec = ctx.specification;
520
+
521
+ // Rule 1: Check context operational within physicalRange
522
+ if ( range && ops ) {
523
+ if ( !allFieldsWithinRange( ops, OPS_LIMIT_FIELDS, range.min, range.max ) ) {
524
+ return false;
525
+ }
526
+ }
527
+
528
+ // Rule 2: Check context specification within physicalRange
529
+ if ( range && spec ) {
530
+ if ( !allFieldsWithinRange( spec, SPEC_LIMIT_FIELDS, range.min, range.max ) ) {
531
+ return false;
532
+ }
533
+ }
534
+
535
+ // Rule 3: Check context specification within context operational
536
+ if ( ops && spec ) {
537
+ if ( !specWithinOperational( ops, spec ) ) return false;
538
+ }
539
+ }
540
+
541
+ return true;
542
+ }; // validContextLimitsHierarchy()
543
+
544
+ /**
545
+ * Schema for individual column definitions.
546
+ *
547
+ * @type {Object}
548
+ */
549
+ const columnSchema = {
550
+ _propertyNames: [
551
+ 'type', 'unit', 'resolution', 'description', 'interpretation',
552
+ 'hysteresis', 'enumRef', 'physicalRange', 'operational',
553
+ 'specification', 'contexts'
554
+ ],
555
+ type: {
556
+ type: 'string',
557
+ required: true,
558
+ validator: validColumnType,
559
+ error: `Column type must be one of: ${COLUMN_TYPES.join( ', ' )}`
560
+ },
561
+ unit: {
562
+ type: 'string',
563
+ required: false,
564
+ default: COLUMN_DEFAULTS.unit
565
+ },
566
+ resolution: {
567
+ type: 'number',
568
+ required: false,
569
+ default: COLUMN_DEFAULTS.resolution,
570
+ validator: validators.positive,
571
+ error: 'Resolution must be a positive number'
572
+ },
573
+ description: {
574
+ type: 'string',
575
+ required: false,
576
+ default: COLUMN_DEFAULTS.description
577
+ },
578
+ interpretation: {
579
+ type: 'array',
580
+ required: false,
581
+ default: COLUMN_DEFAULTS.interpretation,
582
+ validator: validInterpretation,
583
+ error: 'Interpretation must be an array of non-empty strings'
584
+ },
585
+ hysteresis: {
586
+ type: 'number',
587
+ required: false,
588
+ default: COLUMN_DEFAULTS.hysteresis,
589
+ validator: validators.nonNegativeFinite,
590
+ error: 'Hysteresis must be a non-negative finite number'
591
+ },
592
+ enumRef: {
593
+ type: 'string',
594
+ required: false,
595
+ validator: validators.identifier,
596
+ error: 'Enum reference must be a valid identifier'
597
+ },
598
+ physicalRange: {
599
+ type: 'object',
600
+ required: false,
601
+ validator: validPhysicalRange,
602
+ error: 'Physical range must have min < max (both finite numbers)'
603
+ },
604
+ operational: {
605
+ type: 'object',
606
+ required: false,
607
+ validator: validOperational,
608
+ error: 'Operational limits must be in ascending order: criticalLow ≤ warningLow ≤ target ≤ warningHigh ≤ criticalHigh'
609
+ },
610
+ specification: {
611
+ type: 'object',
612
+ required: false,
613
+ validator: validSpecification,
614
+ error: 'Specification must have lowerSpecLimit < upperSpecLimit and target within bounds (if present)'
615
+ },
616
+ contexts: {
617
+ type: 'array',
618
+ required: false,
619
+ default: [],
620
+ itemSchema: {
621
+ type: 'object',
622
+ propertyNames: [ 'when', 'operational', 'specification', 'interpretation' ],
623
+ validator: validContext
624
+ },
625
+ validator: validContexts,
626
+ error: 'Contexts must be an array of valid context entries'
627
+ }
628
+ };
629
+
630
+ // ============================================================================
631
+ // EXPORTS
632
+ // ============================================================================
633
+
634
+ export {
635
+ columnSchema,
636
+ validPhysicalRange,
637
+ validOperational,
638
+ validSpecification,
639
+ validWhen,
640
+ validContext,
641
+ validContexts,
642
+ validColumnType,
643
+ validInterpretation,
644
+ validLimitsMutualExclusivity,
645
+ validLimitsHierarchy,
646
+ validContextLimitsHierarchy,
647
+ COLUMN_TYPES,
648
+ COLUMN_DEFAULTS
649
+ };
650
+
651
+ export default columnSchema;