@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,571 @@
1
+ // core/semantics/loader.js
2
+
3
+ /**
4
+ * @fileoverview Semantics Loader with Cross-Reference Validation
5
+ *
6
+ * Loads and validates semantics JSON files from a config directory.
7
+ * Validation is performed in two phases:
8
+ *
9
+ * 1. Schema validation (via validateWithSchema)
10
+ * - Enum files validated against enumSchema
11
+ * - Asset class files validated against assetClassSchema
12
+ * - Column validation delegated to columnSchema via propertySchema
13
+ *
14
+ * 2. Cross-reference validation (this module)
15
+ * - enumRef → enum must exist
16
+ * - contexts[].when.column → column must exist in same asset class
17
+ * - insightTypes[].columns → all columns must exist in asset class (no duplicates)
18
+ *
19
+ * Fail-fast behavior: throws on first validation error.
20
+ *
21
+ * Platform notes:
22
+ * - Glob patterns use POSIX separators (/) for cross-platform compatibility
23
+ * - File lists are sorted for deterministic load order
24
+ */
25
+
26
+ import { readFile } from 'node:fs/promises';
27
+ import { basename } from 'node:path';
28
+
29
+ // Standard error prefix for all semantic loader errors
30
+ const ERR_PREFIX = 'WinkComposer/semantics: ';
31
+ import { glob } from 'glob';
32
+ import { validateWithSchema } from '../utils/validate/index.js';
33
+ import {
34
+ enumSchema,
35
+ assetClassSchema,
36
+ validLimitsMutualExclusivity,
37
+ validLimitsHierarchy,
38
+ validContextLimitsHierarchy
39
+ } from './schemas/index.js';
40
+ import { computeSemanticsDigest } from './digest.js';
41
+ import { createWarningCollector } from './warnings.js';
42
+
43
+ // ============================================================================
44
+ // CROSS-REFERENCE VALIDATION
45
+ // ============================================================================
46
+
47
+ /**
48
+ * Validates that context.when.column references exist in the asset class.
49
+ *
50
+ * @param {string} assetClassName - Asset class name for error messages
51
+ * @param {string} colName - Column name for error messages
52
+ * @param {Array} contexts - Array of context entries
53
+ * @param {Set} columnNames - Set of valid column names
54
+ * @throws {Error} If a referenced column does not exist
55
+ */
56
+ const validateContextColumnRefs = function ( assetClassName, colName, contexts, columnNames ) {
57
+ for ( let j = 0; j < contexts.length; j += 1 ) {
58
+ const ctx = contexts[ j ];
59
+ // Only validate object-form when clauses (not "default" string)
60
+ if ( ctx.when !== 'default' ) {
61
+ const refColumn = ctx.when.column;
62
+ if ( !columnNames.has( refColumn ) ) {
63
+ throw new Error(
64
+ ERR_PREFIX + `AssetClass '${assetClassName}', column '${colName}', ` +
65
+ `context[${j}]: when.column '${refColumn}' not found in asset class`
66
+ );
67
+ }
68
+ }
69
+ }
70
+ };
71
+
72
+ /**
73
+ * Validate all cross-references within an asset class.
74
+ * Throws on first error for fail-fast behavior.
75
+ *
76
+ * Validates:
77
+ * 1. Mutual exclusivity: contexts vs direct operational/specification
78
+ * 2. Limits hierarchy: physicalRange ⊇ operational ⊇ specification
79
+ * 3. Context limits hierarchy: physicalRange ⊇ ctx.operational ⊇ ctx.specification
80
+ * 4. enumRef → enum exists in loaded enums
81
+ * 5. contexts[].when.column → column exists in same asset class
82
+ * 6. insightTypes[].columns → all exist in asset class
83
+ *
84
+ * @param {string} assetClassName - Name of the asset class
85
+ * @param {Object} assetClass - Asset class definition
86
+ * @param {Object} enums - Loaded enums object
87
+ * @throws {Error} On validation failure
88
+ */
89
+ const validateCrossReferences = function ( assetClassName, assetClass, enums ) {
90
+ const columnNames = new Set( Object.keys( assetClass.columns ) );
91
+
92
+ // Validate each column's cross-references
93
+ const columnEntries = Object.entries( assetClass.columns );
94
+ for ( let i = 0; i < columnEntries.length; i += 1 ) {
95
+ const [ colName, colSpec ] = columnEntries[ i ];
96
+
97
+ // 1. Validate mutual exclusivity: contexts vs direct operational/specification
98
+ if ( !validLimitsMutualExclusivity( colSpec ) ) {
99
+ throw new Error(
100
+ ERR_PREFIX + `AssetClass '${assetClassName}', column '${colName}': ` +
101
+ 'cannot have both contexts and direct operational/specification limits (mutually exclusive)'
102
+ );
103
+ }
104
+
105
+ // 2. Validate limits hierarchy: physicalRange ⊇ operational ⊇ specification
106
+ if ( !validLimitsHierarchy( colSpec ) ) {
107
+ throw new Error(
108
+ ERR_PREFIX + `AssetClass '${assetClassName}', column '${colName}': ` +
109
+ 'operational/specification limits exceed physicalRange bounds or specification exceeds operational bounds'
110
+ );
111
+ }
112
+
113
+ // 3. Validate context limits hierarchy
114
+ if ( !validContextLimitsHierarchy( colSpec ) ) {
115
+ throw new Error(
116
+ ERR_PREFIX + `AssetClass '${assetClassName}', column '${colName}': ` +
117
+ 'context limits exceed physicalRange bounds or context specification exceeds context operational bounds'
118
+ );
119
+ }
120
+
121
+ // 4. Validate enumRef points to existing enum
122
+ if ( colSpec.enumRef !== undefined && !enums[ colSpec.enumRef ] ) {
123
+ throw new Error(
124
+ ERR_PREFIX + `AssetClass '${assetClassName}', column '${colName}': ` +
125
+ `enumRef '${colSpec.enumRef}' not found in loaded enums`
126
+ );
127
+ }
128
+
129
+ // 5. Validate context.when.column exists in same asset class
130
+ if ( colSpec.contexts && colSpec.contexts.length > 0 ) {
131
+ validateContextColumnRefs( assetClassName, colName, colSpec.contexts, columnNames );
132
+ }
133
+ }
134
+
135
+ // 6. Validate insightType columns exist and are unique
136
+ // 7. Validate designatedTimestamp cross-references
137
+ if ( assetClass.insightTypes ) {
138
+ const insightTypeEntries = Object.entries( assetClass.insightTypes );
139
+ for ( let i = 0; i < insightTypeEntries.length; i += 1 ) {
140
+ const [ itName, itSpec ] = insightTypeEntries[ i ];
141
+ const seenColumns = new Set();
142
+ for ( let j = 0; j < itSpec.columns.length; j += 1 ) {
143
+ const colName = itSpec.columns[ j ];
144
+ // Check for duplicate column references
145
+ if ( seenColumns.has( colName ) ) {
146
+ throw new Error(
147
+ ERR_PREFIX + `AssetClass '${assetClassName}', insightType '${itName}': ` +
148
+ `duplicate column '${colName}'`
149
+ );
150
+ }
151
+ seenColumns.add( colName );
152
+ // Check column exists in asset class
153
+ if ( !columnNames.has( colName ) ) {
154
+ throw new Error(
155
+ ERR_PREFIX + `AssetClass '${assetClassName}', insightType '${itName}': ` +
156
+ `column '${colName}' not found in asset class columns`
157
+ );
158
+ }
159
+ }
160
+
161
+ // 7a. Validate designatedTimestamp is in columns list
162
+ if ( !itSpec.columns.includes( itSpec.designatedTimestamp ) ) {
163
+ throw new Error(
164
+ ERR_PREFIX + `AssetClass '${assetClassName}', insightType '${itName}': ` +
165
+ `designatedTimestamp '${itSpec.designatedTimestamp}' not in columns list`
166
+ );
167
+ }
168
+
169
+ // 7b. Validate designatedTimestamp references a timestamp-type column
170
+ const designatedTsColSpec = assetClass.columns[ itSpec.designatedTimestamp ];
171
+ if ( !designatedTsColSpec || designatedTsColSpec.type !== 'timestamp' ) {
172
+ throw new Error(
173
+ ERR_PREFIX + `AssetClass '${assetClassName}', insightType '${itName}': ` +
174
+ `designatedTimestamp '${itSpec.designatedTimestamp}' must reference a column with type 'timestamp'`
175
+ );
176
+ }
177
+ }
178
+ }
179
+ };
180
+
181
+ // ============================================================================
182
+ // NUMERIC TYPE CHECK
183
+ // ============================================================================
184
+
185
+ /**
186
+ * Set of numeric column types.
187
+ * @type {Set<string>}
188
+ */
189
+ const NUMERIC_TYPES = new Set( [ 'float64', 'int64' ] );
190
+
191
+ // ============================================================================
192
+ // COMPLETENESS WARNING FUNCTIONS
193
+ // ============================================================================
194
+
195
+ /**
196
+ * Check for columns missing interpretation (advisory).
197
+ *
198
+ * @param {string} assetClassName - Asset class name for warning messages
199
+ * @param {Object} columns - Columns object
200
+ * @param {Object} warningCollector - Warning collector with add() method
201
+ */
202
+ const checkMissingInterpretation = function ( assetClassName, columns, warningCollector ) {
203
+ const columnNames = Object.keys( columns );
204
+ for ( let i = 0; i < columnNames.length; i += 1 ) {
205
+ const colName = columnNames[ i ];
206
+ const colSpec = columns[ colName ];
207
+ const hasInterp = colSpec.interpretation && colSpec.interpretation.length > 0;
208
+ if ( !hasInterp ) {
209
+ warningCollector.add(
210
+ `AssetClass '${assetClassName}', column '${colName}': missing interpretation`
211
+ );
212
+ }
213
+ }
214
+ };
215
+
216
+ /**
217
+ * Check for numeric columns missing physicalRange (advisory).
218
+ *
219
+ * @param {string} assetClassName - Asset class name for warning messages
220
+ * @param {Object} columns - Columns object
221
+ * @param {Object} warningCollector - Warning collector with add() method
222
+ */
223
+ const checkMissingPhysicalRange = function ( assetClassName, columns, warningCollector ) {
224
+ const columnNames = Object.keys( columns );
225
+ for ( let i = 0; i < columnNames.length; i += 1 ) {
226
+ const colName = columnNames[ i ];
227
+ const colSpec = columns[ colName ];
228
+ if ( NUMERIC_TYPES.has( colSpec.type ) && !colSpec.physicalRange ) {
229
+ warningCollector.add(
230
+ `AssetClass '${assetClassName}', column '${colName}': numeric column missing physicalRange`
231
+ );
232
+ }
233
+ }
234
+ };
235
+
236
+ /**
237
+ * Check for numeric columns missing unit (advisory).
238
+ *
239
+ * @param {string} assetClassName - Asset class name for warning messages
240
+ * @param {Object} columns - Columns object
241
+ * @param {Object} warningCollector - Warning collector with add() method
242
+ */
243
+ const checkMissingUnit = function ( assetClassName, columns, warningCollector ) {
244
+ const columnNames = Object.keys( columns );
245
+ for ( let i = 0; i < columnNames.length; i += 1 ) {
246
+ const colName = columnNames[ i ];
247
+ const colSpec = columns[ colName ];
248
+ const hasUnit = colSpec.unit && colSpec.unit.length > 0;
249
+ if ( NUMERIC_TYPES.has( colSpec.type ) && !hasUnit ) {
250
+ warningCollector.add(
251
+ `AssetClass '${assetClassName}', column '${colName}': numeric column missing unit`
252
+ );
253
+ }
254
+ }
255
+ };
256
+
257
+ /**
258
+ * Check for enums not referenced by any column (advisory).
259
+ *
260
+ * @param {Object} enums - Loaded enums object
261
+ * @param {Object} assetClasses - Loaded asset classes object
262
+ * @param {Object} warningCollector - Warning collector with add() method
263
+ */
264
+ const checkUnreferencedEnums = function ( enums, assetClasses, warningCollector ) {
265
+ const referencedEnums = new Set();
266
+
267
+ // Collect all enumRef values from all asset classes
268
+ const assetClassEntries = Object.entries( assetClasses );
269
+ for ( let i = 0; i < assetClassEntries.length; i += 1 ) {
270
+ const [ , assetClass ] = assetClassEntries[ i ];
271
+ const columnNames = Object.keys( assetClass.columns );
272
+ for ( let j = 0; j < columnNames.length; j += 1 ) {
273
+ const colSpec = assetClass.columns[ columnNames[ j ] ];
274
+ if ( colSpec.enumRef ) {
275
+ referencedEnums.add( colSpec.enumRef );
276
+ }
277
+ }
278
+ }
279
+
280
+ // Check each enum for references
281
+ const enumNames = Object.keys( enums );
282
+ for ( let i = 0; i < enumNames.length; i += 1 ) {
283
+ const enumName = enumNames[ i ];
284
+ if ( !referencedEnums.has( enumName ) ) {
285
+ warningCollector.add(
286
+ `Enum '${enumName}': not referenced by any column`
287
+ );
288
+ }
289
+ }
290
+ };
291
+
292
+ /**
293
+ * Check for columns not used in any insightType (advisory).
294
+ *
295
+ * @param {string} assetClassName - Asset class name for warning messages
296
+ * @param {Object} assetClass - Asset class definition
297
+ * @param {Object} warningCollector - Warning collector with add() method
298
+ */
299
+ const checkUnusedColumns = function ( assetClassName, assetClass, warningCollector ) {
300
+ // Collect all columns used in insightTypes
301
+ const usedColumns = new Set();
302
+ if ( assetClass.insightTypes ) {
303
+ const insightTypeEntries = Object.entries( assetClass.insightTypes );
304
+ for ( let i = 0; i < insightTypeEntries.length; i += 1 ) {
305
+ const [ , itSpec ] = insightTypeEntries[ i ];
306
+ for ( let j = 0; j < itSpec.columns.length; j += 1 ) {
307
+ usedColumns.add( itSpec.columns[ j ] );
308
+ }
309
+ }
310
+ }
311
+
312
+ // Check each column for usage
313
+ const columnNames = Object.keys( assetClass.columns );
314
+ for ( let i = 0; i < columnNames.length; i += 1 ) {
315
+ const colName = columnNames[ i ];
316
+ if ( !usedColumns.has( colName ) ) {
317
+ warningCollector.add(
318
+ `AssetClass '${assetClassName}', column '${colName}': not used in any insightType`
319
+ );
320
+ }
321
+ }
322
+ };
323
+
324
+ /**
325
+ * Check for int64 columns with small value sets that lack enumRef (advisory).
326
+ * This helps identify columns that look like enums but aren't declared as such.
327
+ *
328
+ * Heuristic: if a column is int64 and has physicalRange with max-min < 20,
329
+ * it may represent an enum-like state variable.
330
+ *
331
+ * @param {string} assetClassName - Asset class name for warning messages
332
+ * @param {Object} columns - Columns object
333
+ * @param {Object} warningCollector - Warning collector with add() method
334
+ */
335
+ const checkEnumLikeColumnsWithoutEnumRef = function ( assetClassName, columns, warningCollector ) {
336
+ const MAX_ENUM_RANGE = 20; // Threshold for "small value set"
337
+
338
+ const columnNames = Object.keys( columns );
339
+ for ( let i = 0; i < columnNames.length; i += 1 ) {
340
+ const colName = columnNames[ i ];
341
+ const colSpec = columns[ colName ];
342
+
343
+ // Only check int64 columns without enumRef that have physicalRange
344
+ if ( colSpec.type === 'int64' && !colSpec.enumRef && colSpec.physicalRange ) {
345
+ const range = colSpec.physicalRange.max - colSpec.physicalRange.min;
346
+ if ( range >= 0 && range < MAX_ENUM_RANGE ) {
347
+ warningCollector.add(
348
+ `AssetClass '${assetClassName}', column '${colName}': int64 column with small range ` +
349
+ `(${colSpec.physicalRange.min}-${colSpec.physicalRange.max}) may need enumRef`
350
+ );
351
+ }
352
+ }
353
+ }
354
+ };
355
+
356
+ // ============================================================================
357
+ // FILE LOADING
358
+ // ============================================================================
359
+
360
+ /**
361
+ * Load and parse a JSON file.
362
+ *
363
+ * @param {string} filePath - Path to JSON file
364
+ * @returns {Promise<Object>} Parsed JSON object
365
+ * @throws {Error} On file read or parse failure
366
+ */
367
+ const loadJsonFile = async function ( filePath ) {
368
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- exported helper; the function itself does not validate path provenance, callers do. Current callers (lines ~393, ~433) pass filePaths from glob over composer-configured enumsDir; any future caller must ensure the same before invoking.
369
+ const content = await readFile( filePath, 'utf8' );
370
+ try {
371
+ return JSON.parse( content );
372
+ } catch ( err ) {
373
+ throw new Error( ERR_PREFIX + `Failed to parse JSON file '${filePath}': ${err.message}` );
374
+ }
375
+ };
376
+
377
+ /**
378
+ * Load all enum files from a directory.
379
+ *
380
+ * @param {string} enumsDir - Path to enums directory
381
+ * @returns {Promise<Object>} Map of enum name to enum definition
382
+ * @throws {Error} On validation failure
383
+ */
384
+ const loadEnums = async function ( enumsDir ) {
385
+ // Use POSIX separators for cross-platform glob compatibility
386
+ const pattern = `${enumsDir}/*.json`;
387
+ // Sort for deterministic load order
388
+ const files = ( await glob( pattern ) ).sort();
389
+
390
+ const enums = Object.create( null );
391
+
392
+ for ( let i = 0; i < files.length; i += 1 ) {
393
+ const filePath = files[ i ];
394
+ const data = await loadJsonFile( filePath ); // eslint-disable-line no-await-in-loop
395
+
396
+ // Validate against enum schema
397
+ const validation = validateWithSchema( enumSchema, data, `enum file '${basename( filePath )}'` );
398
+ if ( !validation.valid ) {
399
+ throw new Error(
400
+ ERR_PREFIX + `Enum file '${filePath}' validation failed:\n - ${validation.errors.join( '\n - ' )}`
401
+ );
402
+ }
403
+
404
+ // Check for duplicate enum names
405
+ if ( enums[ data.name ] ) {
406
+ throw new Error(
407
+ ERR_PREFIX + `Duplicate enum name '${data.name}' found in '${filePath}'`
408
+ );
409
+ }
410
+
411
+ enums[ data.name ] = data;
412
+ }
413
+
414
+ return enums;
415
+ };
416
+
417
+ /**
418
+ * Load all asset class files from a directory.
419
+ *
420
+ * @param {string} assetClassesDir - Path to asset-classes directory
421
+ * @returns {Promise<Object>} Map of asset class name to definition
422
+ * @throws {Error} On validation failure
423
+ */
424
+ const loadAssetClasses = async function ( assetClassesDir ) {
425
+ // Use POSIX separators for cross-platform glob compatibility
426
+ const pattern = `${assetClassesDir}/*.json`;
427
+ // Sort for deterministic load order
428
+ const files = ( await glob( pattern ) ).sort();
429
+
430
+ const assetClasses = Object.create( null );
431
+
432
+ for ( let i = 0; i < files.length; i += 1 ) {
433
+ const filePath = files[ i ];
434
+ const data = await loadJsonFile( filePath ); // eslint-disable-line no-await-in-loop
435
+
436
+ // Validate against asset class schema (includes column validation via propertySchema)
437
+ const validation = validateWithSchema( assetClassSchema, data, `asset class file '${basename( filePath )}'` );
438
+ if ( !validation.valid ) {
439
+ throw new Error(
440
+ ERR_PREFIX + `Asset class file '${filePath}' validation failed:\n - ${validation.errors.join( '\n - ' )}`
441
+ );
442
+ }
443
+
444
+ // Check for duplicate asset class names
445
+ if ( assetClasses[ data.name ] ) {
446
+ throw new Error(
447
+ ERR_PREFIX + `Duplicate asset class name '${data.name}' found in '${filePath}'`
448
+ );
449
+ }
450
+
451
+ assetClasses[ data.name ] = data;
452
+ }
453
+
454
+ return assetClasses;
455
+ };
456
+
457
+ // ============================================================================
458
+ // MAIN LOADER
459
+ // ============================================================================
460
+
461
+ /**
462
+ * Load semantics from a config directory with full validation.
463
+ *
464
+ * Directory structure expected:
465
+ * configPath/
466
+ * ├── enums/
467
+ * │ └── *.json
468
+ * └── asset-classes/
469
+ * └── *.json
470
+ *
471
+ * Validation phases:
472
+ * 1. Load and parse JSON files
473
+ * 2. Schema validation (enum, asset-class, column)
474
+ * 3. Cross-reference validation (enumRef, context columns, insightType columns)
475
+ * 4. Completeness warnings (missing interpretation, physicalRange, unit, etc.)
476
+ *
477
+ * @param {string} configPath - Path to semantics config directory
478
+ * @param {Object} [options] - Loading options
479
+ * @param {string[]} [options.assetClasses] - Filter to load only these asset classes by name
480
+ * @param {string} [options.version] - Semantic version string for digest (default: '1.0.0')
481
+ * @param {boolean} [options.suppressWarnings=false] - Suppress all completeness warnings
482
+ * @param {Function} [options.onWarning] - Custom warning handler (msg) => void
483
+ * @returns {Promise<Object>} { enums, assetClasses, digest }
484
+ * @throws {Error} Fail-fast on any validation error
485
+ */
486
+ const loadSemantics = async function ( configPath, options = {} ) {
487
+ const {
488
+ assetClasses: filterNames,
489
+ suppressWarnings = false,
490
+ onWarning
491
+ } = options;
492
+
493
+ // Create warning collector for completeness checks
494
+ const warningCollector = createWarningCollector( { suppressWarnings, onWarning } );
495
+
496
+ // Phase 1-2: Load and validate individual files (schema validation)
497
+ // Use POSIX separators for cross-platform compatibility
498
+ const enumsDir = `${configPath}/enums`;
499
+ const assetClassesDir = `${configPath}/asset-classes`;
500
+
501
+ // Always load all enums (required for enumRef validation)
502
+ const enums = await loadEnums( enumsDir );
503
+
504
+ // Load all asset classes, then filter if requested
505
+ let assetClasses = await loadAssetClasses( assetClassesDir );
506
+
507
+ // Apply filter if provided (strict: unknown names throw)
508
+ if ( filterNames && filterNames.length > 0 ) {
509
+ const filtered = Object.create( null );
510
+ for ( let i = 0; i < filterNames.length; i += 1 ) {
511
+ const name = filterNames[ i ];
512
+ if ( !assetClasses[ name ] ) {
513
+ throw new Error(
514
+ ERR_PREFIX + `Asset class '${name}' not found in loaded definitions`
515
+ );
516
+ }
517
+ filtered[ name ] = assetClasses[ name ];
518
+ }
519
+ assetClasses = filtered;
520
+ }
521
+
522
+ // Phase 3: Cross-reference validation (only on filtered subset)
523
+ const assetClassEntries = Object.entries( assetClasses );
524
+ for ( let i = 0; i < assetClassEntries.length; i += 1 ) {
525
+ const [ name, assetClass ] = assetClassEntries[ i ];
526
+ validateCrossReferences( name, assetClass, enums );
527
+ }
528
+
529
+ // Phase 4: Completeness warnings (advisory, non-blocking)
530
+ for ( let i = 0; i < assetClassEntries.length; i += 1 ) {
531
+ const [ name, assetClass ] = assetClassEntries[ i ];
532
+ checkMissingInterpretation( name, assetClass.columns, warningCollector );
533
+ checkMissingPhysicalRange( name, assetClass.columns, warningCollector );
534
+ checkMissingUnit( name, assetClass.columns, warningCollector );
535
+ checkUnusedColumns( name, assetClass, warningCollector );
536
+ checkEnumLikeColumnsWithoutEnumRef( name, assetClass.columns, warningCollector );
537
+ }
538
+ checkUnreferencedEnums( enums, assetClasses, warningCollector );
539
+
540
+ // Emit accumulated warnings
541
+ warningCollector.emit();
542
+
543
+ // Phase 5: Compute semantic digest
544
+ const digest = computeSemanticsDigest(
545
+ { enums, assetClasses },
546
+ options.version || '1.0.0'
547
+ );
548
+
549
+ return { enums, assetClasses, digest };
550
+ };
551
+
552
+ // ============================================================================
553
+ // EXPORTS
554
+ // ============================================================================
555
+
556
+ export {
557
+ loadSemantics,
558
+ loadEnums,
559
+ loadAssetClasses,
560
+ loadJsonFile,
561
+ validateCrossReferences,
562
+ // Completeness warning functions (used internally, exported for testing)
563
+ checkMissingInterpretation,
564
+ checkMissingPhysicalRange,
565
+ checkMissingUnit,
566
+ checkUnreferencedEnums,
567
+ checkUnusedColumns,
568
+ checkEnumLikeColumnsWithoutEnumRef
569
+ };
570
+
571
+ export default loadSemantics;