@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.
- package/LICENSE +1 -1
- package/README.md +106 -80
- package/package.json +56 -28
- package/src/composer.js +75 -0
- package/src/core/batch/aoo-encoder.js +148 -0
- package/src/core/batch/index.js +12 -0
- package/src/core/batch/sliding-window.js +91 -0
- package/src/core/codec/index.js +137 -0
- package/src/core/emitter-manager/mqtt/constants.js +102 -0
- package/src/core/emitter-manager/mqtt/emitter.js +780 -0
- package/src/core/emitter-manager/mqtt/index.js +190 -0
- package/src/core/emitter-manager/mqtt/mqtt-store.js +538 -0
- package/src/core/emitter-manager/terminal/assert-columns.js +98 -0
- package/src/core/emitter-manager/terminal/formatters.js +160 -0
- package/src/core/emitter-manager/terminal/index.js +341 -0
- package/src/core/env-vars.js +245 -0
- package/src/core/mqtt-protocol.js +47 -0
- package/src/core/partition-manager/index.js +7 -0
- package/src/core/partition-manager/init.js +44 -0
- package/src/core/partition-manager/introspect.js +15 -0
- package/src/core/partition-manager/publish-to.js +6 -0
- package/src/core/partition-manager/recompute.js +5 -0
- package/src/core/partition-manager/reset.js +5 -0
- package/src/core/partition-manager/update.js +264 -0
- package/src/core/partition-manager/validate-flow.js +157 -0
- package/src/core/semantics/digest.js +128 -0
- package/src/core/semantics/index.js +83 -0
- package/src/core/semantics/loader.js +571 -0
- package/src/core/semantics/schemas/asset-class-schema.js +218 -0
- package/src/core/semantics/schemas/column-schema.js +651 -0
- package/src/core/semantics/schemas/enum-schema.js +153 -0
- package/src/core/semantics/schemas/index.js +51 -0
- package/src/core/semantics/warnings.js +141 -0
- package/src/core/shutdown-manager/index.js +289 -0
- package/src/core/source-manager/csv/index.js +171 -0
- package/src/core/source-manager/csv/start.js +456 -0
- package/src/core/source-manager/mqtt/client.js +406 -0
- package/src/core/source-manager/mqtt/constants.js +147 -0
- package/src/core/source-manager/mqtt/dedup.js +244 -0
- package/src/core/source-manager/mqtt/index.js +260 -0
- package/src/core/source-manager/mqtt/status.js +546 -0
- package/src/core/source-manager/test-harness/comparator.js +226 -0
- package/src/core/source-manager/test-harness/field-generator.js +85 -0
- package/src/core/source-manager/test-harness/fuzz.js +87 -0
- package/src/core/source-manager/test-harness/index.js +115 -0
- package/src/core/source-manager/test-harness/prng.js +57 -0
- package/src/core/source-manager/test-harness/start.js +232 -0
- package/src/core/source-manager/test-harness/validate.js +213 -0
- package/src/core/storage-manager/questdb/assert-columns.js +120 -0
- package/src/core/storage-manager/questdb/ensure-tables.js +187 -0
- package/src/core/storage-manager/questdb/index.js +1251 -0
- package/src/core/storage-manager/questdb/persist-plan.js +488 -0
- package/src/core/storage-manager/questdb/writers.js +142 -0
- package/src/core/test-utils/sealed-record.js +80 -0
- package/src/core/test-utils/soak-signature.js +149 -0
- package/src/core/test-utils/tcp-proxy.js +106 -0
- package/src/core/tunable/helpers.js +254 -0
- package/src/core/tunable/index.js +87 -0
- package/src/core/utils/console-formatter/index.js +207 -0
- package/src/core/utils/create-smart-store-as/index.js +98 -0
- package/src/core/utils/create-smart-store-as/sanitize-field.js +46 -0
- package/src/core/utils/create-smart-store-as/smart-truncate.js +93 -0
- package/src/core/utils/flow/index.js +5 -0
- package/src/core/utils/flow/make-collision-checker.js +23 -0
- package/src/core/utils/flow/to-kebab.js +20 -0
- package/src/core/utils/half-life/index.js +111 -0
- package/src/core/utils/node/disable.js +11 -0
- package/src/core/utils/node/enable.js +11 -0
- package/src/core/utils/node/execute-triggers.js +40 -0
- package/src/core/utils/node/index.js +10 -0
- package/src/core/utils/node/pause.js +11 -0
- package/src/core/utils/node/populate-predicate-inputs.js +14 -0
- package/src/core/utils/node/publish-nan.js +19 -0
- package/src/core/utils/node/reset-predicate-inputs.js +11 -0
- package/src/core/utils/node/unpause.js +11 -0
- package/src/core/utils/node/validate-spec.js +22 -0
- package/src/core/utils/options/resolve-field-keyed.js +115 -0
- package/src/core/utils/quantize/index.js +75 -0
- package/src/core/utils/stats/formulas.js +67 -0
- package/src/core/utils/stats/index.js +3 -0
- package/src/core/utils/validate/composer-validators.js +141 -0
- package/src/core/utils/validate/helpers.js +206 -0
- package/src/core/utils/validate/index.js +12 -0
- package/src/core/utils/validate/type-validators.js +538 -0
- package/src/core/utils/validate/validate.js +107 -0
- package/src/core/utils/validate/validators.js +177 -0
- package/src/core/wiring/assert-handle.js +57 -0
- package/src/core/wiring/assert-module.js +63 -0
- package/src/core/wiring/index.js +5 -0
- package/src/core/wiring/node-type-to-module.js +17 -0
- package/src/core/wiring/wire-emitters.js +256 -0
- package/src/core/wiring/wire-linear-graph.js +25 -0
- package/src/core/wiring/wire-node.js +157 -0
- package/src/core/wiring/wire-semantics.js +173 -0
- package/src/core/wiring/wire-storages.js +344 -0
- package/src/flow/check-output-collisions.js +93 -0
- package/src/flow/consts.js +9 -0
- package/src/flow/deep-clone-spec.js +64 -0
- package/src/flow/driver.js +171 -0
- package/src/flow/expand-single-input-array.js +76 -0
- package/src/flow/expand-template-spec.js +93 -0
- package/src/flow/flow-api-schemas.js +300 -0
- package/src/flow/flow.js +658 -0
- package/src/flow/for-each.js +225 -0
- package/src/flow/get-signature-pattern.js +31 -0
- package/src/flow/get-target-array.js +46 -0
- package/src/flow/inspect.js +67 -0
- package/src/flow/load-node-module.js +81 -0
- package/src/flow/make-node-method.js +112 -0
- package/src/flow/resolve-pick-by-field.js +77 -0
- package/src/flow/resolve-tunables.js +76 -0
- package/src/flow/run.js +392 -0
- package/src/flow/serialize.js +161 -0
- package/src/flow/signatures-schema.js +222 -0
- package/src/flow/spec-builder.js +65 -0
- package/src/flow/validate-triggers.js +250 -0
- package/src/flow/validate.js +261 -0
- package/src/nodes/accumulate/index.js +21 -0
- package/src/nodes/accumulate/init.js +48 -0
- package/src/nodes/accumulate/introspect.js +123 -0
- package/src/nodes/accumulate/publish-to.js +34 -0
- package/src/nodes/accumulate/recompute.js +27 -0
- package/src/nodes/accumulate/reset.js +25 -0
- package/src/nodes/accumulate/update.js +42 -0
- package/src/nodes/appraise/calibrate.js +64 -0
- package/src/nodes/appraise/decision.js +66 -0
- package/src/nodes/appraise/deviation.js +140 -0
- package/src/nodes/appraise/index.js +23 -0
- package/src/nodes/appraise/init.js +165 -0
- package/src/nodes/appraise/integrate.js +45 -0
- package/src/nodes/appraise/introspect.js +352 -0
- package/src/nodes/appraise/publish-to.js +93 -0
- package/src/nodes/appraise/receptor.js +69 -0
- package/src/nodes/appraise/recompute.js +79 -0
- package/src/nodes/appraise/reset.js +53 -0
- package/src/nodes/appraise/update.js +130 -0
- package/src/nodes/archive/csv/index.js +6 -0
- package/src/nodes/archive/csv/init.js +36 -0
- package/src/nodes/archive/csv/load.js +70 -0
- package/src/nodes/archive/csv/reset.js +14 -0
- package/src/nodes/archive/csv/simple-parser.js +216 -0
- package/src/nodes/archive/csv/test.js +173 -0
- package/src/nodes/archive/csv/update.js +63 -0
- package/src/nodes/butterworth-filter/index.js +18 -0
- package/src/nodes/butterworth-filter/init.js +184 -0
- package/src/nodes/butterworth-filter/introspect.js +228 -0
- package/src/nodes/butterworth-filter/publish-to.js +21 -0
- package/src/nodes/butterworth-filter/recompute.js +13 -0
- package/src/nodes/butterworth-filter/reset.js +30 -0
- package/src/nodes/butterworth-filter/update.js +38 -0
- package/src/nodes/categorize/index.js +19 -0
- package/src/nodes/categorize/init.js +58 -0
- package/src/nodes/categorize/introspect.js +177 -0
- package/src/nodes/categorize/publish-to.js +29 -0
- package/src/nodes/categorize/recompute.js +14 -0
- package/src/nodes/categorize/reset.js +16 -0
- package/src/nodes/categorize/update.js +53 -0
- package/src/nodes/controller/index.js +9 -0
- package/src/nodes/controller/init.js +55 -0
- package/src/nodes/controller/introspect.js +91 -0
- package/src/nodes/controller/publish-to.js +8 -0
- package/src/nodes/controller/recompute.js +9 -0
- package/src/nodes/controller/reset.js +17 -0
- package/src/nodes/controller/update.js +63 -0
- package/src/nodes/diff/index.js +19 -0
- package/src/nodes/diff/init.js +37 -0
- package/src/nodes/diff/introspect.js +132 -0
- package/src/nodes/diff/publish-to.js +21 -0
- package/src/nodes/diff/recompute.js +12 -0
- package/src/nodes/diff/reset.js +12 -0
- package/src/nodes/diff/update.js +37 -0
- package/src/nodes/digest-moments/formulas.js +12 -0
- package/src/nodes/digest-moments/index.js +14 -0
- package/src/nodes/digest-moments/init.js +67 -0
- package/src/nodes/digest-moments/introspect.js +154 -0
- package/src/nodes/digest-moments/publish-to.js +34 -0
- package/src/nodes/digest-moments/recompute.js +14 -0
- package/src/nodes/digest-moments/reset.js +14 -0
- package/src/nodes/digest-moments/update.js +120 -0
- package/src/nodes/dwell-time-tracker/index.js +14 -0
- package/src/nodes/dwell-time-tracker/init.js +59 -0
- package/src/nodes/dwell-time-tracker/introspect.js +137 -0
- package/src/nodes/dwell-time-tracker/publish-to.js +22 -0
- package/src/nodes/dwell-time-tracker/recompute.js +8 -0
- package/src/nodes/dwell-time-tracker/reset.js +23 -0
- package/src/nodes/dwell-time-tracker/update.js +103 -0
- package/src/nodes/emit-if/helpers.js +94 -0
- package/src/nodes/emit-if/index.js +16 -0
- package/src/nodes/emit-if/init.js +67 -0
- package/src/nodes/emit-if/introspect.js +102 -0
- package/src/nodes/emit-if/publish-to.js +13 -0
- package/src/nodes/emit-if/recompute.js +12 -0
- package/src/nodes/emit-if/reset.js +27 -0
- package/src/nodes/emit-if/update.js +93 -0
- package/src/nodes/es-correlation/index.js +19 -0
- package/src/nodes/es-correlation/init.js +73 -0
- package/src/nodes/es-correlation/introspect.js +176 -0
- package/src/nodes/es-correlation/publish-to.js +32 -0
- package/src/nodes/es-correlation/recompute.js +56 -0
- package/src/nodes/es-correlation/reset.js +27 -0
- package/src/nodes/es-correlation/update.js +110 -0
- package/src/nodes/es-mean/index.js +20 -0
- package/src/nodes/es-mean/init.js +58 -0
- package/src/nodes/es-mean/introspect.js +129 -0
- package/src/nodes/es-mean/publish-to.js +19 -0
- package/src/nodes/es-mean/recompute.js +16 -0
- package/src/nodes/es-mean/reset.js +24 -0
- package/src/nodes/es-mean/update.js +69 -0
- package/src/nodes/es-pairwise-correlation/index.js +19 -0
- package/src/nodes/es-pairwise-correlation/init.js +98 -0
- package/src/nodes/es-pairwise-correlation/introspect.js +187 -0
- package/src/nodes/es-pairwise-correlation/publish-to.js +31 -0
- package/src/nodes/es-pairwise-correlation/recompute.js +69 -0
- package/src/nodes/es-pairwise-correlation/reset.js +29 -0
- package/src/nodes/es-pairwise-correlation/update.js +89 -0
- package/src/nodes/es-stats/index.js +21 -0
- package/src/nodes/es-stats/init.js +105 -0
- package/src/nodes/es-stats/introspect.js +157 -0
- package/src/nodes/es-stats/publish-to.js +42 -0
- package/src/nodes/es-stats/recompute.js +74 -0
- package/src/nodes/es-stats/reset.js +49 -0
- package/src/nodes/es-stats/update-envelope.js +44 -0
- package/src/nodes/es-stats/update-welford.js +64 -0
- package/src/nodes/es-stats/update.js +59 -0
- package/src/nodes/index.js +83 -0
- package/src/nodes/invert-flag/index.js +20 -0
- package/src/nodes/invert-flag/init.js +36 -0
- package/src/nodes/invert-flag/introspect.js +115 -0
- package/src/nodes/invert-flag/publish-to.js +23 -0
- package/src/nodes/invert-flag/recompute.js +12 -0
- package/src/nodes/invert-flag/reset.js +12 -0
- package/src/nodes/invert-flag/update.js +32 -0
- package/src/nodes/kalman1d/index.js +20 -0
- package/src/nodes/kalman1d/init.js +103 -0
- package/src/nodes/kalman1d/introspect.js +250 -0
- package/src/nodes/kalman1d/publish-to.js +34 -0
- package/src/nodes/kalman1d/recompute.js +23 -0
- package/src/nodes/kalman1d/reset.js +29 -0
- package/src/nodes/kalman1d/update.js +144 -0
- package/src/nodes/kernel/index.js +18 -0
- package/src/nodes/kernel/init.js +66 -0
- package/src/nodes/kernel/introspect.js +163 -0
- package/src/nodes/kernel/presets.js +74 -0
- package/src/nodes/kernel/publish-to.js +25 -0
- package/src/nodes/kernel/recompute.js +14 -0
- package/src/nodes/kernel/reset.js +20 -0
- package/src/nodes/kernel/update.js +69 -0
- package/src/nodes/lag/index.js +21 -0
- package/src/nodes/lag/init.js +87 -0
- package/src/nodes/lag/introspect.js +202 -0
- package/src/nodes/lag/publish-to.js +64 -0
- package/src/nodes/lag/recompute.js +33 -0
- package/src/nodes/lag/reset.js +49 -0
- package/src/nodes/lag/update.js +133 -0
- package/src/nodes/median3/index.js +19 -0
- package/src/nodes/median3/init.js +40 -0
- package/src/nodes/median3/introspect.js +116 -0
- package/src/nodes/median3/publish-to.js +21 -0
- package/src/nodes/median3/recompute.js +12 -0
- package/src/nodes/median3/reset.js +15 -0
- package/src/nodes/median3/update.js +58 -0
- package/src/nodes/moments-digest/copy-from.js +12 -0
- package/src/nodes/moments-digest/flush.js +17 -0
- package/src/nodes/moments-digest/index.js +132 -0
- package/src/nodes/moments-digest/init.js +84 -0
- package/src/nodes/moments-digest/introspect.js +122 -0
- package/src/nodes/moments-digest/prelude.js +28 -0
- package/src/nodes/moments-digest/publish-to.js +31 -0
- package/src/nodes/moments-digest/recompute.js +9 -0
- package/src/nodes/moments-digest/reset.js +20 -0
- package/src/nodes/moments-digest/update.js +182 -0
- package/src/nodes/node-names.js +67 -0
- package/src/nodes/page-hinkley/index.js +20 -0
- package/src/nodes/page-hinkley/init.js +66 -0
- package/src/nodes/page-hinkley/introspect.js +193 -0
- package/src/nodes/page-hinkley/publish-to.js +28 -0
- package/src/nodes/page-hinkley/recompute.js +14 -0
- package/src/nodes/page-hinkley/reset.js +23 -0
- package/src/nodes/page-hinkley/update.js +105 -0
- package/src/nodes/pass-if/index.js +21 -0
- package/src/nodes/pass-if/init.js +37 -0
- package/src/nodes/pass-if/introspect.js +93 -0
- package/src/nodes/pass-if/publish-to.js +15 -0
- package/src/nodes/pass-if/recompute.js +15 -0
- package/src/nodes/pass-if/reset.js +18 -0
- package/src/nodes/pass-if/update.js +37 -0
- package/src/nodes/persist-if/helpers.js +111 -0
- package/src/nodes/persist-if/index.js +9 -0
- package/src/nodes/persist-if/init.js +98 -0
- package/src/nodes/persist-if/introspect.js +106 -0
- package/src/nodes/persist-if/publish-to.js +21 -0
- package/src/nodes/persist-if/recompute.js +20 -0
- package/src/nodes/persist-if/reset.js +35 -0
- package/src/nodes/persist-if/update.js +112 -0
- package/src/nodes/persistence-check/index.js +14 -0
- package/src/nodes/persistence-check/init.js +38 -0
- package/src/nodes/persistence-check/introspect.js +144 -0
- package/src/nodes/persistence-check/publish-to.js +11 -0
- package/src/nodes/persistence-check/recompute.js +7 -0
- package/src/nodes/persistence-check/reset.js +12 -0
- package/src/nodes/persistence-check/update.js +57 -0
- package/src/nodes/process-index/index.js +15 -0
- package/src/nodes/process-index/init.js +48 -0
- package/src/nodes/process-index/introspect.js +182 -0
- package/src/nodes/process-index/publish-to.js +29 -0
- package/src/nodes/process-index/recompute.js +8 -0
- package/src/nodes/process-index/reset.js +8 -0
- package/src/nodes/process-index/update.js +75 -0
- package/src/nodes/ratio/index.js +14 -0
- package/src/nodes/ratio/init.js +37 -0
- package/src/nodes/ratio/introspect.js +155 -0
- package/src/nodes/ratio/publish-to.js +15 -0
- package/src/nodes/ratio/recompute.js +7 -0
- package/src/nodes/ratio/reset.js +7 -0
- package/src/nodes/ratio/update.js +47 -0
- package/src/nodes/sanitize/index.js +82 -0
- package/src/nodes/sanitize/init.js +82 -0
- package/src/nodes/sanitize/introspect.js +194 -0
- package/src/nodes/sanitize/publish-to.js +15 -0
- package/src/nodes/sanitize/recompute.js +8 -0
- package/src/nodes/sanitize/reset.js +14 -0
- package/src/nodes/sanitize/update.js +102 -0
- package/src/nodes/spike-guard/index.js +14 -0
- package/src/nodes/spike-guard/init.js +47 -0
- package/src/nodes/spike-guard/introspect.js +130 -0
- package/src/nodes/spike-guard/publish-to.js +35 -0
- package/src/nodes/spike-guard/recompute.js +14 -0
- package/src/nodes/spike-guard/reset.js +19 -0
- package/src/nodes/spike-guard/update.js +89 -0
- package/src/nodes/sse-emitter/index.js +7 -0
- package/src/nodes/sse-emitter/init.js +105 -0
- package/src/nodes/sse-emitter/introspect.js +134 -0
- package/src/nodes/sse-emitter/publish-to.js +6 -0
- package/src/nodes/sse-emitter/recompute.js +16 -0
- package/src/nodes/sse-emitter/reset.js +19 -0
- package/src/nodes/sse-emitter/update.js +69 -0
- package/src/nodes/sse-emitter/utils/broadcast-heartbeat.js +36 -0
- package/src/nodes/sse-emitter/utils/build-message.js +31 -0
- package/src/nodes/sse-emitter/utils/connection-manager.js +45 -0
- package/src/nodes/sse-emitter/utils/endpoint-initializer.js +20 -0
- package/src/nodes/sse-emitter/utils/health-monitor.js +38 -0
- package/src/nodes/sse-emitter/utils/qos-handler.js +41 -0
- package/src/nodes/state-change-detector/helpers.js +35 -0
- package/src/nodes/state-change-detector/index.js +12 -0
- package/src/nodes/state-change-detector/init.js +56 -0
- package/src/nodes/state-change-detector/introspect.js +146 -0
- package/src/nodes/state-change-detector/publish-to.js +20 -0
- package/src/nodes/state-change-detector/recompute.js +6 -0
- package/src/nodes/state-change-detector/reset.js +20 -0
- package/src/nodes/state-change-detector/update.js +97 -0
- package/src/nodes/sw-stats/index.js +14 -0
- package/src/nodes/sw-stats/init.js +51 -0
- package/src/nodes/sw-stats/introspect.js +131 -0
- package/src/nodes/sw-stats/publish-to.js +45 -0
- package/src/nodes/sw-stats/recompute.js +33 -0
- package/src/nodes/sw-stats/reset.js +15 -0
- package/src/nodes/sw-stats/update.js +39 -0
- package/src/nodes/swing-watch/index.js +17 -0
- package/src/nodes/swing-watch/init.js +144 -0
- package/src/nodes/swing-watch/introspect.js +188 -0
- package/src/nodes/swing-watch/publish-to.js +53 -0
- package/src/nodes/swing-watch/recompute.js +22 -0
- package/src/nodes/swing-watch/reset.js +55 -0
- package/src/nodes/swing-watch/update.js +767 -0
- package/src/nodes/tally/index.js +15 -0
- package/src/nodes/tally/init.js +32 -0
- package/src/nodes/tally/introspect.js +116 -0
- package/src/nodes/tally/publish-to.js +28 -0
- package/src/nodes/tally/recompute.js +10 -0
- package/src/nodes/tally/reset.js +10 -0
- package/src/nodes/tally/update.js +44 -0
- package/src/nodes/threshold/index.js +14 -0
- package/src/nodes/threshold/init.js +121 -0
- package/src/nodes/threshold/introspect.js +192 -0
- package/src/nodes/threshold/publish-to.js +11 -0
- package/src/nodes/threshold/recompute.js +8 -0
- package/src/nodes/threshold/reset.js +14 -0
- package/src/nodes/threshold/update.js +54 -0
- package/src/nodes/transform/helpers.js +39 -0
- package/src/nodes/transform/index.js +15 -0
- package/src/nodes/transform/init.js +35 -0
- package/src/nodes/transform/introspect.js +118 -0
- package/src/nodes/transform/publish-to.js +23 -0
- package/src/nodes/transform/recompute.js +12 -0
- package/src/nodes/transform/reset.js +11 -0
- package/src/nodes/transform/update.js +32 -0
- package/src/nodes/trend/compute-confidence.js +112 -0
- package/src/nodes/trend/index.js +14 -0
- package/src/nodes/trend/init.js +81 -0
- package/src/nodes/trend/introspect.js +151 -0
- package/src/nodes/trend/publish-to.js +22 -0
- package/src/nodes/trend/recompute.js +15 -0
- package/src/nodes/trend/reset.js +26 -0
- package/src/nodes/trend/update.js +93 -0
- package/src/nodes/tw-stats/copy-from.js +16 -0
- package/src/nodes/tw-stats/flush.js +12 -0
- package/src/nodes/tw-stats/index.js +41 -0
- package/src/nodes/tw-stats/init.js +92 -0
- package/src/nodes/tw-stats/introspect.js +171 -0
- package/src/nodes/tw-stats/publish-to.js +108 -0
- package/src/nodes/tw-stats/recompute.js +9 -0
- package/src/nodes/tw-stats/reset.js +16 -0
- package/src/nodes/tw-stats/update.js +113 -0
- package/src/nodes/unbalance/index.js +15 -0
- package/src/nodes/unbalance/init.js +59 -0
- package/src/nodes/unbalance/introspect.js +167 -0
- package/src/nodes/unbalance/publish-to.js +52 -0
- package/src/nodes/unbalance/recompute.js +11 -0
- package/src/nodes/unbalance/reset.js +10 -0
- package/src/nodes/unbalance/update.js +83 -0
- package/src/nodes/vector-distance/index.js +14 -0
- package/src/nodes/vector-distance/init.js +54 -0
- package/src/nodes/vector-distance/introspect.js +149 -0
- package/src/nodes/vector-distance/publish-to.js +35 -0
- package/src/nodes/vector-distance/recompute.js +11 -0
- package/src/nodes/vector-distance/reset.js +20 -0
- package/src/nodes/vector-distance/update.js +139 -0
- package/src/nodes/winnow/index.js +12 -0
- package/src/nodes/winnow/init.js +82 -0
- package/src/nodes/winnow/introspect.js +253 -0
- package/src/nodes/winnow/publish-to.js +51 -0
- package/src/nodes/winnow/recompute.js +14 -0
- package/src/nodes/winnow/reset.js +34 -0
- package/src/nodes/winnow/update.js +203 -0
- package/src/tools/training/features.js +227 -0
- package/src/tools/training/index.js +21 -0
- package/src/tools/training/logistic.js +238 -0
- package/src/tools/training/metrics.js +126 -0
- package/src/tools/training/scale.js +239 -0
- package/src/tools/training/validate.js +103 -0
- package/src/windowing/count-sliding/create.js +16 -0
- package/src/windowing/count-sliding/get-reverse-chronological-index.js +19 -0
- package/src/windowing/count-sliding/index.js +6 -0
- package/src/windowing/count-sliding/is-not-full.js +5 -0
- package/src/windowing/count-sliding/push.js +15 -0
- package/src/windowing/count-sliding/reset.js +11 -0
- package/src/windowing/count-sliding/size.js +5 -0
- package/src/windowing/time-sliding/create.js +25 -0
- package/src/windowing/time-sliding/push.js +42 -0
- package/CODE_OF_CONDUCT.md +0 -46
- package/CONTRIBUTING.md +0 -132
- package/docs/.nojekyll +0 -0
- package/docs/CONTRIBUTING.md +0 -134
- package/docs/README.md +0 -132
- package/docs/_coverpage.md +0 -26
- package/docs/_media/bg.png +0 -0
- package/docs/_media/wink-logo.png +0 -0
- package/docs/_navbar.md +0 -14
- package/docs/_sidebar.md +0 -2
- package/docs/index.html +0 -44
- 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 };
|