@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,232 @@
1
+ // core/source-manager/test-harness/start.js
2
+
3
+ /**
4
+ * @fileoverview Synthetic source for end-to-end contract checks.
5
+ *
6
+ * testHarness invents messages on demand and feeds them to the
7
+ * pipeline. It does not read from a file or a network — it is a
8
+ * verification adapter, used by integration tests to push messages
9
+ * through every sink and check that all sinks agree.
10
+ *
11
+ * Each generated message carries a running number `_harnessId` so
12
+ * the cross-sink check tool can find the same message in every
13
+ * sink, regardless of arrival order. Field values come from the
14
+ * test author's `messageTemplate`. Tests can opt into a small set
15
+ * of fuzz patterns that inject bad values every Nth message; with
16
+ * fuzz on, the only check is "no adapter crashed".
17
+ *
18
+ * Adapter contract (ADR-018, source role):
19
+ * - Every `onStatus` payload carries the uniform structured shape per
20
+ * ADR-018: `{status: 'green' | 'yellow' | 'red', connected,
21
+ * phase}`; the error path adds `error: {code, message}`. One payload
22
+ * rule, no exceptions (uniformity sweep, 2026-07-09).
23
+ * - Lifecycle phases:
24
+ * `phase: 'starting'` — harness is about to begin
25
+ * `phase: 'generating'` — first message about to be sent
26
+ * `phase: 'complete'` — all messages sent (`connected: false`);
27
+ * carries the uniform `count` of produced
28
+ * messages (per ADR-018 there is no
29
+ * onComplete callback)
30
+ * `phase: 'errored'` — terminal red: the run loop failed and
31
+ * generation stopped (the ADR-018 two-tier
32
+ * rule)
33
+ * `phase: 'stopped'` — yellow status when forced to stop
34
+ *
35
+ * `err.code` vocabulary (per-adapter, listed here per ADR-018):
36
+ * - `INVALID_CONFIG` — setup-time; bad messageTemplate or asset
37
+ * class. Thrown synchronously from start().
38
+ * - `GENERATOR_ERROR` — runtime; a downstream `onMessage` call
39
+ * threw inside the loop. Routed through
40
+ * onStatus, not re-thrown.
41
+ *
42
+ * Design decisions date from 2026-04-29. The `messageTemplate` shape
43
+ * is enforced at startup by `validate.js`; the per-field spec shape
44
+ * is documented in `field-generator.js`.
45
+ */
46
+
47
+ import { createPrng } from './prng.js';
48
+ import { generateField } from './field-generator.js';
49
+ import { applyFuzz, FUZZ_PATTERN_NAMES } from './fuzz.js';
50
+ import { validateMessageTemplate, validateAssetClass } from './validate.js';
51
+
52
+ const DEFAULT_MESSAGE_COUNT = 1000;
53
+ const DEFAULT_INTERVAL_MS = 0;
54
+ const DEFAULT_FUZZ_INTERVAL = 0;
55
+ const DEFAULT_STOP_TIMEOUT = 5000;
56
+
57
+ /**
58
+ * Starts the synthetic source.
59
+ *
60
+ * @param {Object} config
61
+ * @param {Object} config.messageTemplate - Required. Shape enforced by validate.js.
62
+ * @param {Object} config.assetClass - Required. Must declare `_harnessId` as int64.
63
+ * @param {Function} config.onMessage - Required. Pipeline-injected handler.
64
+ * @param {Function} [config.onStatus] - Status callback (structured shape per ADR-018);
65
+ * completion arrives as `{phase: 'complete', count}`.
66
+ * @param {Function} [config.onShutdown] - Pipeline shutdown hook (injected by runtime).
67
+ * @param {boolean} [config.shutdownOnComplete=true] - Auto-shutdown pipeline when generation ends.
68
+ * @returns {Function} stopFn — accepts `{ timeout }`, returns Promise resolving when stopped.
69
+ */
70
+ export const start = function ( config ) {
71
+ const {
72
+ messageTemplate,
73
+ assetClass,
74
+ onMessage,
75
+ onStatus = null,
76
+ onShutdown = null,
77
+ shutdownOnComplete = true
78
+ } = config;
79
+
80
+ // Setup-time validation. Both throw INVALID_CONFIG on any
81
+ // problem, listing every issue found.
82
+ validateMessageTemplate( messageTemplate );
83
+ validateAssetClass( assetClass );
84
+
85
+ if ( typeof onMessage !== 'function' ) {
86
+ const err = new Error( 'testHarness: onMessage must be a function' );
87
+ err.code = 'INVALID_CONFIG';
88
+ throw err;
89
+ }
90
+
91
+ // Pull out template options with defaults.
92
+ const seed = messageTemplate.seed;
93
+ const messageCount = messageTemplate.messageCount ?? DEFAULT_MESSAGE_COUNT;
94
+ const intervalMs = messageTemplate.intervalMs ?? DEFAULT_INTERVAL_MS;
95
+ const fuzzInterval = messageTemplate.fuzzInterval ?? DEFAULT_FUZZ_INTERVAL;
96
+ const fuzzTarget = messageTemplate.fuzzTarget ?? null;
97
+ const fields = messageTemplate.fields;
98
+ const fieldNames = Object.keys( fields );
99
+
100
+ // Stop / finished bookkeeping. `finished` resolves when the run
101
+ // loop fully exits (success or error) — the stop function races
102
+ // it against the timeout, mirroring CSV's shape (ADR-018).
103
+ const prng = createPrng( seed );
104
+ let stopped = false;
105
+ let sentCount = 0;
106
+ let resolveFinished;
107
+ const finished = new Promise( ( resolve ) => {
108
+ resolveFinished = resolve;
109
+ } );
110
+
111
+ if ( onStatus ) onStatus( {
112
+ status: 'green',
113
+ connected: true,
114
+ phase: 'starting',
115
+ messageCount,
116
+ fuzzInterval
117
+ } );
118
+
119
+ const run = async function () {
120
+ if ( onStatus ) onStatus( {
121
+ status: 'green',
122
+ connected: true,
123
+ phase: 'generating'
124
+ } );
125
+
126
+ for ( let i = 1; i <= messageCount; i += 1 ) {
127
+ if ( stopped ) break;
128
+
129
+ const msg = Object.create( null );
130
+ msg._harnessId = i; // eslint-disable-line no-underscore-dangle
131
+
132
+ for ( const name of fieldNames ) {
133
+ msg[ name ] = generateField( fields[ name ], prng, i, intervalMs );
134
+ }
135
+
136
+ // Fuzz: inject one bad value every Nth message.
137
+ if ( fuzzInterval > 0 && i % fuzzInterval === 0 ) {
138
+ const fuzzNumber = i / fuzzInterval;
139
+ const patternIndex = ( fuzzNumber - 1 ) % FUZZ_PATTERN_NAMES.length;
140
+ applyFuzz( msg, fuzzTarget, fields[ fuzzTarget ], patternIndex );
141
+ }
142
+
143
+ // eslint-disable-next-line no-await-in-loop -- Source must wait for the consumer before sending the next message; flooding the consumer would defeat the purpose of an integration check.
144
+ await onMessage( msg );
145
+ sentCount = i;
146
+
147
+ if ( intervalMs > 0 && !stopped ) {
148
+ // eslint-disable-next-line no-await-in-loop -- Paced send between messages is part of the contract for tests that exercise idle-flush timing.
149
+ await new Promise( ( r ) => setTimeout( r, intervalMs ) );
150
+ }
151
+ }
152
+
153
+ // Completion travels onStatus with the uniform `count` field
154
+ // (per ADR-018 there is no onComplete callback).
155
+ if ( onStatus ) onStatus( {
156
+ status: 'green',
157
+ connected: false,
158
+ phase: 'complete',
159
+ count: sentCount
160
+ } );
161
+
162
+ if ( shutdownOnComplete && onShutdown ) {
163
+ await onShutdown();
164
+ }
165
+ };
166
+
167
+ // Kick off. Failures inside the loop come from a downstream
168
+ // onMessage handler (the harness itself has no transport that
169
+ // can fail). Route through onStatus so the test sees the error
170
+ // without losing it. Always resolve `finished` so stop() can
171
+ // wait on it.
172
+ run().catch( ( err ) => {
173
+ const message = ( err && err.message ) ? err.message : String( err );
174
+ if ( onStatus ) {
175
+ // Terminal red: generation stopped. Uniform payload with
176
+ // phase 'errored' per the ADR-018 two-tier rule.
177
+ onStatus( {
178
+ status: 'red',
179
+ connected: false,
180
+ phase: 'errored',
181
+ error: { code: 'GENERATOR_ERROR', message }
182
+ } );
183
+ } else {
184
+ console.error( `testHarness error [GENERATOR_ERROR]: ${message}` );
185
+ }
186
+ } ).finally( () => {
187
+ resolveFinished();
188
+ } );
189
+
190
+ // Stop function with `{ timeout }` symmetry per ADR-018.
191
+ //
192
+ // What it does:
193
+ // - Asks the loop to stop at the next message by setting `stopped`.
194
+ // - Waits for the loop to finish. If it finishes within `timeout`
195
+ // ms, the stop returns right away.
196
+ // - If the loop is stuck inside an `await onMessage(msg)` that
197
+ // never returns, the time budget runs out. The stop returns
198
+ // anyway so the rest of the shutdown can proceed.
199
+ // - When the budget runs out, sends a yellow status so callers
200
+ // can see the stop was forced rather than clean.
201
+ //
202
+ // The timer is `unref()`ed so it does not keep Node alive while
203
+ // the loop is finishing in time.
204
+ return function ( { timeout = DEFAULT_STOP_TIMEOUT } = {} ) {
205
+ stopped = true;
206
+ return new Promise( ( resolve ) => {
207
+ let settled = false;
208
+ let forceTimer = null;
209
+ const settle = function () {
210
+
211
+ /* c8 ignore next -- defensive: in normal lifecycle, settle is invoked exactly once (timer fires OR finished resolves; clean path clears the timer before it can fire). The guard protects against a race that requires both paths to fire concurrently, which has no producer in the current design. */
212
+ if ( settled ) return;
213
+ settled = true;
214
+ if ( forceTimer ) clearTimeout( forceTimer );
215
+ resolve();
216
+ };
217
+ forceTimer = setTimeout( () => {
218
+ if ( onStatus ) {
219
+ onStatus( {
220
+ status: 'yellow',
221
+ connected: false,
222
+ phase: 'stopped',
223
+ note: `Stop took longer than ${timeout}ms — forced.`
224
+ } );
225
+ }
226
+ settle();
227
+ }, timeout );
228
+ forceTimer.unref();
229
+ finished.then( settle, settle );
230
+ } );
231
+ };
232
+ };
@@ -0,0 +1,213 @@
1
+ // core/source-manager/test-harness/validate.js
2
+
3
+ /**
4
+ * @fileoverview Startup validation for testHarness.
5
+ *
6
+ * Two things to check before the harness starts running:
7
+ *
8
+ * 1. The `messageTemplate` config — it must declare a seed, at least
9
+ * one field, valid types, and (when fuzz is on) a target column.
10
+ * 2. The asset class — it must declare `_harnessId` as an int64
11
+ * column. The cross-sink check tool uses this number to find the
12
+ * same message in every sink, so every sink must be able to
13
+ * store and report it.
14
+ *
15
+ * Validation runs through the shared `validateWithSchema` helper
16
+ * (see `core/utils/validate`). On any failure we re-throw as a single
17
+ * Error with `code = 'INVALID_CONFIG'` — the classified shape
18
+ * ADR-018 mandates for setup failures. The message lists every
19
+ * problem found so
20
+ * the test author can fix them in one pass.
21
+ */
22
+
23
+ import { validateWithSchema, validators } from '../../utils/validate/index.js';
24
+
25
+ const FIELD_TYPES = [ 'float64', 'int64', 'bool', 'string', 'timestamp' ];
26
+
27
+ /**
28
+ * Schema for one entry in messageTemplate.fields.
29
+ */
30
+ const FIELD_SPEC_SCHEMA = {
31
+ type: 'object',
32
+ properties: {
33
+ type: {
34
+ type: 'string',
35
+ required: true,
36
+ validator: validators.oneOf( FIELD_TYPES ),
37
+ error: `must be one of ${FIELD_TYPES.join( ', ' )}`
38
+ },
39
+ range: {
40
+ type: 'array',
41
+ minItems: 2,
42
+ maxItems: 2,
43
+ itemSchema: { type: 'number' },
44
+ validator: function ( arr ) {
45
+ return arr[ 0 ] < arr[ 1 ];
46
+ },
47
+ error: 'must be [ min, max ] with min < max'
48
+ },
49
+ resolution: {
50
+ type: 'number',
51
+ validator: validators.positive,
52
+ error: 'must be a positive number'
53
+ },
54
+ values: {
55
+ type: 'array',
56
+ minItems: 1
57
+ },
58
+ mode: {
59
+ type: 'string',
60
+ validator: validators.oneOf( [ 'monotonic-ms', 'static' ] ),
61
+ error: 'must be "monotonic-ms" or "static"'
62
+ }
63
+ // `seedValue` accepts any type, so we omit it from the schema.
64
+ }
65
+ };
66
+
67
+ /**
68
+ * Schema for the messageTemplate config.
69
+ *
70
+ * Cross-field rules (handled by `_crossFieldValidators`):
71
+ * - fuzzTarget is required when fuzzInterval > 0
72
+ * - fuzzTarget must name a declared field
73
+ * - fields must not declare `_harnessId` (the harness adds it)
74
+ */
75
+ export const MESSAGE_TEMPLATE_SCHEMA = {
76
+ seed: {
77
+ type: 'number',
78
+ required: true,
79
+ validator: validators.isFinite,
80
+ error: 'seed must be a finite number'
81
+ },
82
+ messageCount: {
83
+ type: 'number',
84
+ validator: validators.positiveInteger,
85
+ error: 'messageCount must be a positive integer'
86
+ },
87
+ intervalMs: {
88
+ type: 'number',
89
+ validator: validators.nonNegativeFinite,
90
+ error: 'intervalMs must be a non-negative finite number'
91
+ },
92
+ fuzzInterval: {
93
+ type: 'number',
94
+ validator: function ( v ) {
95
+ return Number.isInteger( v ) && v >= 0;
96
+ },
97
+ error: 'fuzzInterval must be a non-negative integer'
98
+ },
99
+ fuzzTarget: {
100
+ type: 'string'
101
+ },
102
+ fields: {
103
+ type: 'object',
104
+ required: true,
105
+ minProperties: 1,
106
+ propertySchema: FIELD_SPEC_SCHEMA
107
+ },
108
+ _crossFieldValidators: [
109
+ {
110
+ fields: [ 'fuzzInterval', 'fuzzTarget' ],
111
+ validator: function ( obj ) {
112
+ if ( !( obj.fuzzInterval > 0 ) ) return true;
113
+ return typeof obj.fuzzTarget === 'string' && obj.fuzzTarget.length > 0;
114
+ },
115
+ error: 'fuzzTarget is required when fuzzInterval > 0'
116
+ },
117
+ {
118
+ fields: [ 'fuzzTarget', 'fields' ],
119
+ validator: function ( obj ) {
120
+ if ( !obj.fuzzTarget ) return true;
121
+ return obj.fuzzTarget in obj.fields;
122
+ },
123
+ error: 'fuzzTarget must name a declared field'
124
+ },
125
+ {
126
+ fields: [ 'fields' ],
127
+ validator: function ( obj ) {
128
+ return !( '_harnessId' in obj.fields );
129
+ },
130
+ error: 'fields must not declare "_harnessId" — the harness adds it automatically'
131
+ },
132
+ {
133
+ fields: [ 'fields' ],
134
+ validator: function ( obj ) {
135
+ for ( const spec of Object.values( obj.fields ) ) {
136
+ if ( spec.type === 'string' ) {
137
+ if ( !Array.isArray( spec.values ) || spec.values.length === 0 ) {
138
+ return false;
139
+ }
140
+ }
141
+ }
142
+ return true;
143
+ },
144
+ error: 'fields with type "string" must declare a non-empty values: [ ... ] list'
145
+ }
146
+ ]
147
+ };
148
+
149
+ /**
150
+ * Schema for the asset class. Only checks the parts the harness
151
+ * cares about — `_harnessId` declared as an int64 column. Other
152
+ * columns may exist; we do not look at them here.
153
+ */
154
+ export const ASSET_CLASS_SCHEMA = {
155
+ columns: {
156
+ type: 'object',
157
+ required: true,
158
+ properties: {
159
+ _harnessId: {
160
+ type: 'object',
161
+ required: true,
162
+ properties: {
163
+ type: {
164
+ type: 'string',
165
+ required: true,
166
+ value: 'int64',
167
+ error: 'must be int64'
168
+ }
169
+ }
170
+ }
171
+ }
172
+ }
173
+ };
174
+
175
+ const throwInvalidConfig = function ( errors ) {
176
+ const err = new Error( `testHarness: ${errors.join( '; ' )}` );
177
+ err.code = 'INVALID_CONFIG';
178
+ throw err;
179
+ };
180
+
181
+ /**
182
+ * Validates the messageTemplate. Throws Error with
183
+ * `code = 'INVALID_CONFIG'` if anything fails.
184
+ *
185
+ * @param {Object} template - messageTemplate from config
186
+ */
187
+ export const validateMessageTemplate = function ( template ) {
188
+ if ( !template || typeof template !== 'object' ) {
189
+ throwInvalidConfig( [ 'messageTemplate is required (object)' ] );
190
+ }
191
+ const result = validateWithSchema( MESSAGE_TEMPLATE_SCHEMA, template, 'messageTemplate' );
192
+ if ( !result.valid ) {
193
+ throwInvalidConfig( result.errors );
194
+ }
195
+ };
196
+
197
+ /**
198
+ * Validates the asset class. The harness needs `_harnessId` as an
199
+ * int64 column — the check tool uses this number to find the same
200
+ * message across sinks. Throws Error with
201
+ * `code = 'INVALID_CONFIG'` if anything fails.
202
+ *
203
+ * @param {Object} assetClass - The asset class declared by the test
204
+ */
205
+ export const validateAssetClass = function ( assetClass ) {
206
+ if ( !assetClass || typeof assetClass !== 'object' ) {
207
+ throwInvalidConfig( [ 'assetClass is required in config' ] );
208
+ }
209
+ const result = validateWithSchema( ASSET_CLASS_SCHEMA, assetClass, 'assetClass' );
210
+ if ( !result.valid ) {
211
+ throwInvalidConfig( result.errors );
212
+ }
213
+ };
@@ -0,0 +1,120 @@
1
+ // core/storage-manager/questdb/assert-columns.js
2
+
3
+ /**
4
+ * @fileoverview Walks every column in an asset class and checks that the
5
+ * column-internal facts QuestDB actually reads are well-formed. Throws
6
+ * with `err.code = 'INVALID_CONFIG'` on the first problem found, naming
7
+ * the offending column and field in the message.
8
+ *
9
+ * Why this file exists separately from the universal semantics schema:
10
+ *
11
+ * The semantics schema (in `core/semantics/schemas/`) validates what a
12
+ * well-formed asset class looks like in the abstract. It says "every
13
+ * column needs a `type`" because that is true for any consumer; it does
14
+ * NOT say "if `type === 'float64'` then `resolution` must be positive"
15
+ * because that is QuestDB-specific knowledge — other adapters may not
16
+ * care about resolution at all. Adapter-specific contracts belong in
17
+ * the adapter, not in the universal schema.
18
+ *
19
+ * Per ADR-018's column-internal facts pattern: each adapter that
20
+ * consumes column-internal facts MUST validate them defensively at
21
+ * startup AND document those assertions in its `@fileoverview`. This
22
+ * file is QuestDB's enforcement point; the documentation lives in the
23
+ * `Column-internal facts consumed` section of `index.js`'s top-of-file
24
+ * comment block.
25
+ *
26
+ * What this layer catches that the universal semantics schema does not:
27
+ *
28
+ * - **Type drift.** The universal schema accepts any type the semantics
29
+ * layer knows about. QuestDB knows how to write a fixed set (the keys
30
+ * of `DDL_TYPES` — float64, int64, bool, string, timestamp). If a new
31
+ * type joins semantics tomorrow but QuestDB's writers haven't caught
32
+ * up, the silent fallback (in `ensure-tables.js` and `persist-plan.js`)
33
+ * would coerce values to VARCHAR. This check makes the gap loud.
34
+ * - **Bypassed schema validation.** Any path that builds an asset class
35
+ * without going through `loadSemantics()` AND without going through
36
+ * `flow.assetClass()` (e.g., a test calling `createStorage` directly)
37
+ * skips the schema. This file is the last line of defense.
38
+ * - **Resolution sanity for float64.** Resolution is optional in the
39
+ * schema (defaults to 1, treated as passthrough by `writers.js:54`).
40
+ * But if the caller declares `resolution: 0` or `resolution: -0.1`,
41
+ * the writer would compute `invResolution = 1 / resolution` as
42
+ * Infinity / negative — silently corrupting every persisted value.
43
+ * The schema does not enforce positivity (since absent resolution is
44
+ * valid); this layer does, when present.
45
+ *
46
+ * Family: same shape as `core/wiring/assert-handle.js`. Both are pure
47
+ * functions that throw diagnostically at startup when an adapter
48
+ * contract is violated.
49
+ *
50
+ * `err.code` thrown:
51
+ * - `INVALID_CONFIG` — column-internal fact missing or malformed.
52
+ * Per ADR-018, setup-time throws carry a classified `err.code`.
53
+ *
54
+ * @see ADR-018 (capability declaration)
55
+ * @see ./writers.js (resolution handling at write time)
56
+ */
57
+
58
+ import { DDL_TYPES } from './ensure-tables.js';
59
+
60
+ /**
61
+ * Walk every column in `assetClass.columns` and assert the facts QuestDB
62
+ * reads are well-formed. Throws on the first violation.
63
+ *
64
+ * Behaviour:
65
+ * - Each column's `type` must be set and writable by QuestDB
66
+ * (i.e., a key in `DDL_TYPES`).
67
+ * - For columns where `type === 'float64'` and `resolution` is defined,
68
+ * `resolution` must be a positive finite number. (Absent resolution
69
+ * is fine — the writer treats undefined as passthrough.)
70
+ *
71
+ * @param {Object} assetClass - asset class with `columns` map
72
+ * @throws {Error} `err.code === 'INVALID_CONFIG'` on any column-internal
73
+ * fact violation; message names the column and the offending field
74
+ */
75
+ const assertColumnFacts = function ( assetClass ) {
76
+ const columns = assetClass.columns || Object.create( null );
77
+ const colNames = Object.keys( columns );
78
+
79
+ for ( let i = 0; i < colNames.length; i += 1 ) {
80
+ const colName = colNames[ i ];
81
+ const colSpec = columns[ colName ];
82
+
83
+ // Column type must be set and writable by QuestDB. Closes the
84
+ // silent fallback to VARCHAR / writeAsString for any missing or
85
+ // unsupported type. The `colSpec` truthy check also catches a
86
+ // null / undefined column entry — caller bug, but we surface it
87
+ // clearly rather than crashing on `.type` access.
88
+ if ( !colSpec || !( colSpec.type in DDL_TYPES ) ) {
89
+ const supportedTypes = Object.keys( DDL_TYPES ).join( ', ' );
90
+ const actualType = colSpec ? String( colSpec.type ) : 'undefined';
91
+ const err = new Error(
92
+ `WinkComposer/questdb: column '${colName}' has invalid or missing type '${actualType}'; ` +
93
+ `expected one of: ${supportedTypes}`
94
+ );
95
+ err.code = 'INVALID_CONFIG';
96
+ throw err;
97
+ }
98
+
99
+ // Float64 columns may declare a resolution for quantization. When
100
+ // present, must be a positive finite number — invResolution at
101
+ // 0 / negative / non-finite would silently corrupt every value.
102
+ // Absent resolution is fine: writers.js:54 treats undefined and
103
+ // 1 as passthrough (no quantization).
104
+ if ( colSpec.type === 'float64' && colSpec.resolution !== undefined ) {
105
+ if ( typeof colSpec.resolution !== 'number' ||
106
+ !Number.isFinite( colSpec.resolution ) ||
107
+ colSpec.resolution <= 0 ) {
108
+ const err = new Error(
109
+ `WinkComposer/questdb: column '${colName}' has type 'float64' ` +
110
+ `but resolution is '${colSpec.resolution}'; ` +
111
+ 'expected positive finite number (or omit for passthrough)'
112
+ );
113
+ err.code = 'INVALID_CONFIG';
114
+ throw err;
115
+ }
116
+ }
117
+ }
118
+ };
119
+
120
+ export { assertColumnFacts };