@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,538 @@
1
+ /**
2
+ * Type-specific validators
3
+ * Each validator returns an array of error messages
4
+ */
5
+
6
+ // ============================================================================
7
+ // DRY HELPERS - Shared validation logic
8
+ // ============================================================================
9
+
10
+ /**
11
+ * Validates numeric value against schema constraints.
12
+ * Used by number, numberOrFieldKeyed, numberOrFunction, etc.
13
+ *
14
+ * @param {number} value - Already type-checked number
15
+ * @param {Object} schema - Schema with min, max, integer
16
+ * @param {string} path - Error path
17
+ * @returns {string[]} Array of error messages
18
+ */
19
+ const validateNumericConstraints = function ( value, schema, path ) {
20
+ const errors = [];
21
+
22
+ if ( Number.isNaN( value ) ) {
23
+ return [ `${path}: Value is NaN` ];
24
+ }
25
+
26
+ if ( schema.min !== undefined && value < schema.min ) {
27
+ errors.push( `${path}: Minimum value is ${schema.min}, got ${value}` );
28
+ }
29
+
30
+ if ( schema.max !== undefined && value > schema.max ) {
31
+ errors.push( `${path}: Maximum value is ${schema.max}, got ${value}` );
32
+ }
33
+
34
+ if ( schema.integer && !Number.isInteger( value ) ) {
35
+ errors.push( `${path}: Expected integer, got ${value}` );
36
+ }
37
+
38
+ return errors;
39
+ }; // validateNumericConstraints()
40
+
41
+ /**
42
+ * Validates function arity against schema.
43
+ *
44
+ * @param {Function} fn - Already type-checked function
45
+ * @param {Object} schema - Schema with optional arity
46
+ * @param {string} path - Error path
47
+ * @returns {string[]} Array of error messages
48
+ */
49
+ const validateFunctionArity = function ( fn, schema, path ) {
50
+ if ( schema.arity !== undefined && fn.length !== schema.arity ) {
51
+ const plural = schema.arity === 1 ? 'parameter' : 'parameters';
52
+ return [ `${path}: Expected function with ${schema.arity} ${plural}, got ${fn.length}` ];
53
+ }
54
+ return [];
55
+ }; // validateFunctionArity()
56
+
57
+ /**
58
+ * Validates array constraints.
59
+ *
60
+ * @param {Array} arr - Already type-checked array
61
+ * @param {Object} schema - Schema with minItems, maxItems
62
+ * @param {string} path - Error path
63
+ * @returns {string[]} Array of error messages
64
+ */
65
+ const validateArrayConstraints = function ( arr, schema, path ) {
66
+ const errors = [];
67
+
68
+ if ( schema.minItems !== undefined && arr.length < schema.minItems ) {
69
+ errors.push( `${path}: Minimum items is ${schema.minItems}, got ${arr.length}` );
70
+ }
71
+
72
+ if ( schema.maxItems !== undefined && arr.length > schema.maxItems ) {
73
+ errors.push( `${path}: Maximum items is ${schema.maxItems}, got ${arr.length}` );
74
+ }
75
+
76
+ return errors;
77
+ }; // validateArrayConstraints()
78
+
79
+ /**
80
+ * Validates one nested object against an inner property schema.
81
+ *
82
+ * The inner shape is read from `schema.properties` — for a range that is
83
+ * `{ min: { type: 'number', required: true }, max: { type: 'number', required: true } }`.
84
+ * Each declared property is checked for presence when required, and numeric
85
+ * properties are checked against their numeric constraints. The key names come
86
+ * from the schema, so this is not tied to min/max — any nested object of
87
+ * numbers works.
88
+ *
89
+ * @param {*} obj - The candidate nested object
90
+ * @param {Object} schema - Schema whose `properties` declares the inner shape
91
+ * @param {string} path - Error path
92
+ * @returns {string[]} Array of error messages
93
+ */
94
+ const validateNestedObjectShape = function ( obj, schema, path ) {
95
+ if ( typeof obj !== 'object' || obj === null || Array.isArray( obj ) ) {
96
+ const actual = obj === null ? 'null' : ( Array.isArray( obj ) ? 'array' : typeof obj );
97
+ return [ `${path}: Expected object, got ${actual}` ];
98
+ }
99
+
100
+ const errors = [];
101
+ const props = schema.properties || {};
102
+ Object.entries( props ).forEach( ( [ key, propSchema ] ) => {
103
+ const present = key in obj;
104
+ if ( !present ) {
105
+ if ( propSchema.required ) {
106
+ errors.push( `${path}.${key}: Required field missing` );
107
+ }
108
+ return;
109
+ }
110
+ const val = obj[ key ];
111
+ if ( propSchema.type === 'number' && typeof val !== 'number' ) {
112
+ errors.push( `${path}.${key}: Expected number, got ${typeof val}` );
113
+ return;
114
+ }
115
+ if ( propSchema.type === 'number' ) {
116
+ errors.push( ...validateNumericConstraints( val, propSchema, `${path}.${key}` ) );
117
+ }
118
+ } );
119
+ return errors;
120
+ }; // validateNestedObjectShape()
121
+
122
+ // ============================================================================
123
+ // TYPE VALIDATORS
124
+ // ============================================================================
125
+
126
+ /**
127
+ * String type validator
128
+ */
129
+ const string = function ( value, schema, path ) {
130
+ const errors = [];
131
+
132
+ if ( typeof value !== 'string' ) {
133
+ return [ `${path}: Expected string, got ${typeof value}` ];
134
+ }
135
+
136
+ if ( schema.minLength !== undefined && value.length < schema.minLength ) {
137
+ errors.push( `${path}: Minimum length is ${schema.minLength}, got ${value.length}` );
138
+ }
139
+
140
+ if ( schema.maxLength !== undefined && value.length > schema.maxLength ) {
141
+ errors.push( `${path}: Maximum length is ${schema.maxLength}, got ${value.length}` );
142
+ }
143
+
144
+ if ( schema.pattern && !schema.pattern.test( value ) ) {
145
+ errors.push( `${path}: Value does not match required pattern` );
146
+ }
147
+
148
+ return errors;
149
+ }; // string()
150
+
151
+ /**
152
+ * Number type validator
153
+ */
154
+ const number = function ( value, schema, path ) {
155
+ if ( typeof value !== 'number' ) {
156
+ return [ `${path}: Expected number, got ${typeof value}` ];
157
+ }
158
+
159
+ return validateNumericConstraints( value, schema, path );
160
+ }; // number()
161
+
162
+ /**
163
+ * Boolean type validator
164
+ */
165
+ const boolean = function ( value, schema, path ) {
166
+ if ( typeof value !== 'boolean' ) {
167
+ return [ `${path}: Expected boolean, got ${typeof value}` ];
168
+ }
169
+ return [];
170
+ }; // boolean()
171
+
172
+ /**
173
+ * Function type validator
174
+ */
175
+ const functionType = function ( value, schema, path ) {
176
+ if ( typeof value !== 'function' ) {
177
+ return [ `${path}: Expected function, got ${typeof value}` ];
178
+ }
179
+
180
+ return validateFunctionArity( value, schema, path );
181
+ }; // functionType()
182
+
183
+ /**
184
+ * Array type validator
185
+ */
186
+ const array = function ( value, schema, path ) {
187
+ if ( !Array.isArray( value ) ) {
188
+ return [ `${path}: Expected array, got ${typeof value}` ];
189
+ }
190
+
191
+ // Note: Item validation is handled in validateField to avoid circular dependency
192
+ return validateArrayConstraints( value, schema, path );
193
+ }; // array()
194
+
195
+ const arrayOrString = function ( value, schema, path ) {
196
+ const errors = [];
197
+
198
+ if ( !( Array.isArray( value ) || typeof value === 'string' ) ) {
199
+ return [ `${path}: Expected array or string, got ${typeof value}` ];
200
+ }
201
+
202
+ if ( schema.minItems !== undefined && value.length < schema.minItems ) {
203
+ errors.push( `${path}: Minimum items is ${schema.minItems}, got ${value.length}` );
204
+ }
205
+ // Note: Item validation is handled in validateField to avoid circular dependency
206
+
207
+ return errors;
208
+ }; // arrayOrString()
209
+
210
+ // ── Per-field map types (internally "field-keyed") ──────────────────────────
211
+ // The validators below accept an option written as a per-field map: an object
212
+ // from field name to that field's value, e.g. { temp: 5, pressure: 10 }. One
213
+ // node reading several fields can then give each field its own value.
214
+ //
215
+ // "field-keyed" is the internal name for this shape — the map's keys are field
216
+ // names. User-facing docs and the error strings below say "per-field map"
217
+ // instead: same thing, plainer words. Keep that wording in any new error text.
218
+ // ────────────────────────────────────────────────────────────────────────────
219
+
220
+ /**
221
+ * Number or field-keyed object containing numbers.
222
+ * Accepts: 5, { temp: 5, pressure: 10 }
223
+ * Used for tunable parameters that support per-field configuration.
224
+ */
225
+ const numberOrFieldKeyed = function ( value, schema, path ) {
226
+ // Direct number case
227
+ if ( typeof value === 'number' ) {
228
+ return number( value, schema, path );
229
+ }
230
+
231
+ // Field-keyed object case
232
+ if ( typeof value === 'object' && value !== null && !Array.isArray( value ) ) {
233
+ const errors = [];
234
+ for ( const [ key, val ] of Object.entries( value ) ) {
235
+ if ( typeof val === 'number' ) {
236
+ errors.push( ...validateNumericConstraints( val, schema, `${path}.${key}` ) );
237
+ } else {
238
+ errors.push( `${path}.${key}: Expected number, got ${typeof val}` );
239
+ }
240
+ }
241
+ return errors;
242
+ }
243
+
244
+ return [ `${path}: Expected number or per-field map of numbers, got ${typeof value}` ];
245
+ }; // numberOrFieldKeyed()
246
+
247
+ /**
248
+ * Array or field-keyed object containing arrays.
249
+ * Accepts: [1,2,3], { temp: [1,2], pressure: [3,4] }
250
+ * Used for parameters like thresholds, categories that support per-field configuration.
251
+ */
252
+ const arrayOrFieldKeyed = function ( value, schema, path ) {
253
+ // Direct array case
254
+ if ( Array.isArray( value ) ) {
255
+ return array( value, schema, path );
256
+ }
257
+
258
+ // Field-keyed object case
259
+ if ( typeof value === 'object' && value !== null ) {
260
+ const errors = [];
261
+ for ( const [ key, val ] of Object.entries( value ) ) {
262
+ if ( Array.isArray( val ) ) {
263
+ errors.push( ...validateArrayConstraints( val, schema, `${path}.${key}` ) );
264
+ } else {
265
+ errors.push( `${path}.${key}: Expected array, got ${typeof val}` );
266
+ }
267
+ }
268
+ return errors;
269
+ }
270
+
271
+ return [ `${path}: Expected array or per-field map of arrays, got ${typeof value}` ];
272
+ }; // arrayOrFieldKeyed()
273
+
274
+ /**
275
+ * String or field-keyed object containing strings.
276
+ * Accepts: 'lowpass', { temp: 'lowpass', pressure: 'highpass' }
277
+ * Used for parameters like filterType, preset that support per-field configuration.
278
+ */
279
+ const stringOrFieldKeyed = function ( value, schema, path ) {
280
+ // Direct string case
281
+ if ( typeof value === 'string' ) {
282
+ return string( value, schema, path );
283
+ }
284
+
285
+ // Field-keyed object case
286
+ if ( typeof value === 'object' && value !== null && !Array.isArray( value ) ) {
287
+ const errors = [];
288
+ for ( const [ key, val ] of Object.entries( value ) ) {
289
+ if ( typeof val === 'string' ) {
290
+ // Apply schema constraints to each string
291
+ if ( schema.minLength !== undefined && val.length < schema.minLength ) {
292
+ errors.push( `${path}.${key}: Minimum length is ${schema.minLength}, got ${val.length}` );
293
+ }
294
+ if ( schema.maxLength !== undefined && val.length > schema.maxLength ) {
295
+ errors.push( `${path}.${key}: Maximum length is ${schema.maxLength}, got ${val.length}` );
296
+ }
297
+ if ( schema.pattern && !schema.pattern.test( val ) ) {
298
+ errors.push( `${path}.${key}: Value does not match required pattern` );
299
+ }
300
+ } else {
301
+ errors.push( `${path}.${key}: Expected string, got ${typeof val}` );
302
+ }
303
+ }
304
+ return errors;
305
+ }
306
+
307
+ return [ `${path}: Expected string or per-field map of strings, got ${typeof value}` ];
308
+ }; // stringOrFieldKeyed()
309
+
310
+ /**
311
+ * Number or function type validator.
312
+ * Accepts: 5, (msg) => msg.stdev * 0.5
313
+ * Used for tunable parameters that support dynamic (tunable) values.
314
+ *
315
+ * @param {*} value - Value to validate
316
+ * @param {Object} schema - Schema with optional arity for function validation
317
+ * @param {string} path - Path for error messages
318
+ * @returns {string[]} Array of error messages
319
+ */
320
+ const numberOrFunction = function ( value, schema, path ) {
321
+ // Function case
322
+ if ( typeof value === 'function' ) {
323
+ return validateFunctionArity( value, schema, path );
324
+ }
325
+
326
+ // Number case
327
+ if ( typeof value === 'number' ) {
328
+ return number( value, schema, path );
329
+ }
330
+
331
+ return [ `${path}: Expected number or function, got ${typeof value}` ];
332
+ }; // numberOrFunction()
333
+
334
+ /**
335
+ * Validate a single field value that can be number or function.
336
+ * Helper for numberOrFunctionOrFieldKeyed to keep nesting manageable.
337
+ */
338
+ const validateNumericOrFunctionField = function ( val, schema, fieldPath ) {
339
+ if ( typeof val === 'function' ) {
340
+ return validateFunctionArity( val, schema, fieldPath );
341
+ }
342
+ if ( typeof val !== 'number' ) {
343
+ return [ `${fieldPath}: Expected number or function, got ${typeof val}` ];
344
+ }
345
+ return validateNumericConstraints( val, schema, fieldPath );
346
+ }; // validateNumericOrFunctionField()
347
+
348
+ /**
349
+ * Number, function, or field-keyed object containing numbers/functions.
350
+ * Accepts: 5, (msg) => msg.value, { temp: 5, pressure: (msg) => msg.baseline }
351
+ * Used for tunable parameters that support both field-keying and tunables.
352
+ */
353
+ const numberOrFunctionOrFieldKeyed = function ( value, schema, path ) {
354
+ // Function case (top priority - dynamic at top level)
355
+ if ( typeof value === 'function' ) {
356
+ return validateFunctionArity( value, schema, path );
357
+ }
358
+
359
+ // Direct number case
360
+ if ( typeof value === 'number' ) {
361
+ return number( value, schema, path );
362
+ }
363
+
364
+ // Field-keyed object case (values can be numbers or functions)
365
+ if ( typeof value === 'object' && value !== null && !Array.isArray( value ) ) {
366
+ const errors = [];
367
+ for ( const [ key, val ] of Object.entries( value ) ) {
368
+ errors.push( ...validateNumericOrFunctionField( val, schema, `${path}.${key}` ) );
369
+ }
370
+ return errors;
371
+ }
372
+
373
+ return [ `${path}: Expected number, function, or per-field map of numbers/functions, got ${typeof value}` ];
374
+ }; // numberOrFunctionOrFieldKeyed()
375
+
376
+ /**
377
+ * Validate a single field value that can be array or function.
378
+ * Helper for arrayOrFunctionOrFieldKeyed to keep nesting manageable.
379
+ */
380
+ const validateArrayOrFunctionField = function ( val, schema, fieldPath ) {
381
+ if ( typeof val === 'function' ) {
382
+ return validateFunctionArity( val, schema, fieldPath );
383
+ }
384
+ if ( !Array.isArray( val ) ) {
385
+ return [ `${fieldPath}: Expected array or function, got ${typeof val}` ];
386
+ }
387
+ return validateArrayConstraints( val, schema, fieldPath );
388
+ }; // validateArrayOrFunctionField()
389
+
390
+ /**
391
+ * Array, function, or field-keyed object containing arrays/functions.
392
+ * Accepts: [1,2,3], (msg) => [...], { temp: [1,2], pressure: (msg) => [...] }
393
+ * Used for parameters like thresholds that support field-keying and tunables.
394
+ */
395
+ const arrayOrFunctionOrFieldKeyed = function ( value, schema, path ) {
396
+ // Function case (top priority - dynamic at top level)
397
+ if ( typeof value === 'function' ) {
398
+ return validateFunctionArity( value, schema, path );
399
+ }
400
+
401
+ // Direct array case
402
+ if ( Array.isArray( value ) ) {
403
+ return array( value, schema, path );
404
+ }
405
+
406
+ // Field-keyed object case (values can be arrays or functions)
407
+ if ( typeof value === 'object' && value !== null ) {
408
+ const errors = [];
409
+ for ( const [ key, val ] of Object.entries( value ) ) {
410
+ errors.push( ...validateArrayOrFunctionField( val, schema, `${path}.${key}` ) );
411
+ }
412
+ return errors;
413
+ }
414
+
415
+ return [ `${path}: Expected array, function, or per-field map of arrays/functions, got ${typeof value}` ];
416
+ }; // arrayOrFunctionOrFieldKeyed()
417
+
418
+ /**
419
+ * Nested object, function, or field-keyed map of nested objects.
420
+ * Accepts: { min: 0, max: 100 },
421
+ * { temp: { min: -40, max: 85 }, pressure: { min: 0, max: 120 } },
422
+ * (msg) => ({ min: msg.lo, max: msg.hi })
423
+ * Used for a structured option (like sanitize ranges) that the runtime resolver
424
+ * already accepts in either a direct or a field-keyed shape. The inner shape
425
+ * (which keys, numeric) comes from `schema.properties`.
426
+ *
427
+ * Direct and field-keyed are told apart the way the runtime resolver does
428
+ * (`resolveNestedObject` in resolve-field-keyed.js): if the object itself
429
+ * carries the inner keys it is direct; otherwise its values are the nested
430
+ * objects. A field whose name collides with an inner key (a field literally
431
+ * named "min") is the one ambiguous case; real field names do not.
432
+ */
433
+ const nestedObjectOrFunctionOrFieldKeyed = function ( value, schema, path ) {
434
+ // Function case (tunable) — dynamic at the top level
435
+ if ( typeof value === 'function' ) {
436
+ return validateFunctionArity( value, schema, path );
437
+ }
438
+
439
+ // The remaining two shapes must both be plain (non-array) objects
440
+ if ( typeof value !== 'object' || value === null || Array.isArray( value ) ) {
441
+ const actual = value === null ? 'null' : ( Array.isArray( value ) ? 'array' : typeof value );
442
+ return [ `${path}: Expected object, function, or per-field map, got ${actual}` ];
443
+ }
444
+
445
+ // Direct shape: the object itself carries the inner keys (e.g. min/max)
446
+ const innerKeys = Object.keys( schema.properties || {} );
447
+ const isDirect = innerKeys.some( ( k ) => k in value );
448
+ if ( isDirect ) {
449
+ return validateNestedObjectShape( value, schema, path );
450
+ }
451
+
452
+ // Field-keyed shape: every value is a nested object
453
+ const errors = [];
454
+ Object.entries( value ).forEach( ( [ key, val ] ) => {
455
+ errors.push( ...validateNestedObjectShape( val, schema, `${path}.${key}` ) );
456
+ } );
457
+ return errors;
458
+ }; // nestedObjectOrFunctionOrFieldKeyed()
459
+
460
+ /**
461
+ * Object type validator
462
+ */
463
+ const object = function ( value, schema, path ) {
464
+ const errors = [];
465
+
466
+ if ( typeof value !== 'object' || value === null ) {
467
+ const actualType = value === null ? 'null' : typeof value;
468
+ return [ `${path}: Expected object, got ${actualType}` ];
469
+ }
470
+
471
+ if ( Array.isArray( value ) ) {
472
+ return [ `${path}: Expected object, got array` ];
473
+ }
474
+
475
+ const keyCount = Object.keys( value ).length;
476
+
477
+ if ( schema.minProperties !== undefined && keyCount < schema.minProperties ) {
478
+ errors.push( `${path}: Minimum properties is ${schema.minProperties}, got ${keyCount}` );
479
+ }
480
+
481
+ if ( schema.maxProperties !== undefined && keyCount > schema.maxProperties ) {
482
+ errors.push( `${path}: Maximum properties is ${schema.maxProperties}, got ${keyCount}` );
483
+ }
484
+
485
+ // Note: Property validation is handled in validateField to avoid circular dependency
486
+
487
+ return errors;
488
+ }; // object()
489
+
490
+ /**
491
+ * Object or function type validator.
492
+ * Accepts: { min: 0, max: 100 }, (msg) => ({ min: msg.minVal, max: msg.maxVal })
493
+ * Used for nested object parameters that support tunable values (e.g., ranges).
494
+ */
495
+ const objectOrFunction = function ( value, schema, path ) {
496
+ // Function case
497
+ if ( typeof value === 'function' ) {
498
+ return validateFunctionArity( value, schema, path );
499
+ }
500
+
501
+ // Object case - delegate to object validator
502
+ return object( value, schema, path );
503
+ }; // objectOrFunction()
504
+
505
+ // Types whose DIRECT form is never a plain (non-array) object, so a plain object
506
+ // value is unambiguously the field-keyed shape ({ field: value, ... }). The
507
+ // validation engine uses this to decide when a custom validator must run per entry
508
+ // of a field-keyed map rather than on the whole map.
509
+ //
510
+ // Deliberately excludes nestedObjectOrFunctionOrFieldKeyed: its direct form IS an
511
+ // object, so a plain object cannot be told apart from a field-keyed map by shape
512
+ // alone. (It carries no custom validator today, so per-entry dispatch is moot.)
513
+ export const FIELD_KEYED_TYPES = new Set( [
514
+ 'numberOrFieldKeyed',
515
+ 'stringOrFieldKeyed',
516
+ 'arrayOrFieldKeyed',
517
+ 'numberOrFunctionOrFieldKeyed',
518
+ 'arrayOrFunctionOrFieldKeyed'
519
+ ] );
520
+
521
+ // Export all type validators
522
+ export const typeValidators = {
523
+ string,
524
+ number,
525
+ boolean,
526
+ function: functionType,
527
+ array,
528
+ object,
529
+ arrayOrString,
530
+ numberOrFieldKeyed,
531
+ arrayOrFieldKeyed,
532
+ stringOrFieldKeyed,
533
+ numberOrFunction,
534
+ numberOrFunctionOrFieldKeyed,
535
+ arrayOrFunctionOrFieldKeyed,
536
+ nestedObjectOrFunctionOrFieldKeyed,
537
+ objectOrFunction
538
+ };
@@ -0,0 +1,107 @@
1
+ /* eslint-disable no-underscore-dangle */
2
+ /**
3
+ * Core validation logic
4
+ * Validates objects against schema definitions with composable validators
5
+ */
6
+
7
+ import { validateField } from './helpers.js';
8
+
9
+ const VALIDATION_VERSION = '1.0.0';
10
+
11
+ /**
12
+ * Runs cross-field validators
13
+ */
14
+ const runCrossFieldValidators = function ( object, validators ) {
15
+ const errors = [];
16
+
17
+ validators.forEach( ( { fields, validator, error } ) => {
18
+ try {
19
+ const isValid = validator( object );
20
+ if ( !isValid ) {
21
+ errors.push( error );
22
+ }
23
+ } catch ( e ) {
24
+ const fieldList = fields.join( ', ' );
25
+ errors.push( `Cross-field validation (${fieldList}) threw error: ${e.message}` );
26
+ }
27
+ } );
28
+
29
+ return errors;
30
+ }; // runCrossFieldValidators()
31
+
32
+ /**
33
+ * Validates an object against a schema definition
34
+ * @param {Object} schema - Schema definition with field specifications
35
+ * @param {Object} object - Object to validate
36
+ * @param {string} pathPrefix - Prefix for error paths (e.g., 'spec')
37
+ * @returns {Object} Validation result with valid flag, errors array, and throwIfInvalid method
38
+ */
39
+ const validateWithSchema = function ( schema, object, pathPrefix = 'object' ) {
40
+ const errors = [];
41
+
42
+ // Validate each field defined in schema
43
+ Object.entries( schema ).forEach( ( [ key, fieldSchema ] ) => {
44
+ // Skip special keys
45
+ if ( key.startsWith( '_' ) ) return;
46
+
47
+ const fieldPath = `${pathPrefix}.${key}`;
48
+
49
+ // Check required fields
50
+ if ( fieldSchema.required && !(key in object) ) {
51
+ errors.push( `${fieldPath}: Required field missing` );
52
+ return; // Skip further validation for missing required fields
53
+ }
54
+
55
+ // Validate present fields
56
+ if ( key in object ) {
57
+ const fieldErrors = validateField(
58
+ object[ key ],
59
+ fieldSchema,
60
+ fieldPath,
61
+ object // Pass full object for cross-references
62
+ );
63
+ errors.push( ...fieldErrors );
64
+ }
65
+ } );
66
+
67
+ // Check for unknown properties at top level
68
+ if ( schema._propertyNames ) {
69
+ const allowedKeys = new Set( schema._propertyNames );
70
+ const objectKeys = Object.keys( object );
71
+ for ( let i = 0; i < objectKeys.length; i += 1 ) {
72
+ const key = objectKeys[ i ];
73
+ if ( !allowedKeys.has( key ) ) {
74
+ errors.push( `${pathPrefix}: Unknown property '${key}'` );
75
+ }
76
+ }
77
+ }
78
+
79
+ // Run cross-field validators only if field validation passed
80
+ if ( errors.length === 0 && schema._crossFieldValidators ) {
81
+ const crossFieldErrors = runCrossFieldValidators(
82
+ object,
83
+ schema._crossFieldValidators
84
+ );
85
+ errors.push( ...crossFieldErrors );
86
+ }
87
+
88
+ // Build result object
89
+ const valid = errors.length === 0;
90
+
91
+ return {
92
+ valid,
93
+ errors,
94
+ version: VALIDATION_VERSION,
95
+ // Throw if invalid - used in init functions
96
+ throwIfInvalid: ( nodeType ) => {
97
+ if ( !valid ) {
98
+ const errorMessage = `WinkComposer/${nodeType} validation failed:\n ${errors.join( '\n ' )}`;
99
+ throw new TypeError( errorMessage );
100
+ }
101
+ }
102
+ };
103
+ }; // validateWithSchema()
104
+
105
+ // Export the function directly
106
+ export default validateWithSchema;
107
+ export { validateWithSchema };