@winkjs/composer 0.0.2 → 0.4.0
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 +42 -103
- package/package.json +53 -29
- 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,155 @@
|
|
|
1
|
+
// nodes/ratio/introspect.js
|
|
2
|
+
|
|
3
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
4
|
+
|
|
5
|
+
/** Single source of truth for what this plugin can do */
|
|
6
|
+
const SUPPORTED_STATS = [
|
|
7
|
+
'ratio'
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
/** Human-readable descriptions (for introspection) */
|
|
11
|
+
const STAT_DESCRIPTIONS = {
|
|
12
|
+
ratio: 'Ratio of x to y (x/y), optionally scaled or in decibels'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** Control methods available for triggers */
|
|
16
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
17
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
18
|
+
enable: 'Enables node processing',
|
|
19
|
+
disable: 'Disables node processing',
|
|
20
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
21
|
+
unpause: 'Resumes node processing after pause'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/** The type of this node */
|
|
25
|
+
const NODE_TYPE = 'Ratio';
|
|
26
|
+
|
|
27
|
+
/** Node capabilities for documentation */
|
|
28
|
+
const CAPABILITIES = {
|
|
29
|
+
description: 'Computes the ratio of two numeric fields with optional logarithmic scale',
|
|
30
|
+
features: [
|
|
31
|
+
'Linear ratio computation (x/y)',
|
|
32
|
+
'Linear scaling of ratio output (x/y × k) for unit conversion',
|
|
33
|
+
'Decibel scale output (20*log₁₀(x/y)) for SNR and gain measurements',
|
|
34
|
+
'Numerically stable logarithm implementation',
|
|
35
|
+
'Division-by-zero protection with configurable minimum threshold',
|
|
36
|
+
'NaN propagation for invalid inputs'
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Attach static metadata once, from our constants by making a shallow copy.
|
|
41
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
42
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
43
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
44
|
+
export const getNodeType = () => NODE_TYPE;
|
|
45
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
46
|
+
|
|
47
|
+
export const DEFAULT_OPTIONS = {
|
|
48
|
+
logScale: false,
|
|
49
|
+
minY: 1e-10,
|
|
50
|
+
scaleBy: 1
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
// DSL Metadata for transpilation
|
|
54
|
+
const DSL_METADATA = {
|
|
55
|
+
// Spec schema for validation
|
|
56
|
+
specSchema: {
|
|
57
|
+
nodeType: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
required: true,
|
|
60
|
+
value: NODE_TYPE
|
|
61
|
+
},
|
|
62
|
+
name: {
|
|
63
|
+
type: 'string',
|
|
64
|
+
required: true,
|
|
65
|
+
validator: validators.identifier,
|
|
66
|
+
error: 'Name must be a valid identifier'
|
|
67
|
+
},
|
|
68
|
+
from: {
|
|
69
|
+
type: 'object',
|
|
70
|
+
required: true,
|
|
71
|
+
properties: {
|
|
72
|
+
x: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
required: true,
|
|
75
|
+
validator: validators.noSpaces,
|
|
76
|
+
error: 'Field name cannot contain spaces'
|
|
77
|
+
},
|
|
78
|
+
y: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
required: true,
|
|
81
|
+
validator: validators.noSpaces,
|
|
82
|
+
error: 'Field name cannot contain spaces'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
logScale: {
|
|
87
|
+
type: 'boolean',
|
|
88
|
+
required: false,
|
|
89
|
+
default: false
|
|
90
|
+
},
|
|
91
|
+
minY: {
|
|
92
|
+
type: 'number',
|
|
93
|
+
required: false,
|
|
94
|
+
default: 1e-10, // Prevents division by extremely small numbers
|
|
95
|
+
validator: (value) => value >= 0,
|
|
96
|
+
error: 'minY must be non-negative'
|
|
97
|
+
},
|
|
98
|
+
scaleBy: {
|
|
99
|
+
type: 'number',
|
|
100
|
+
required: false,
|
|
101
|
+
default: 1,
|
|
102
|
+
validator: ( value ) => value > 0,
|
|
103
|
+
error: 'scaleBy must be a positive number'
|
|
104
|
+
},
|
|
105
|
+
stats: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
required: true,
|
|
108
|
+
propertyNames: SUPPORTED_STATS,
|
|
109
|
+
propertySchema: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
required: true,
|
|
112
|
+
properties: {
|
|
113
|
+
storeAs: {
|
|
114
|
+
type: 'string',
|
|
115
|
+
required: true,
|
|
116
|
+
minLength: 1,
|
|
117
|
+
validator: validators.identifier,
|
|
118
|
+
error: 'storeAs must be a valid identifier'
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
124
|
+
|
|
125
|
+
// Cross-field validators
|
|
126
|
+
crossFieldValidators: [
|
|
127
|
+
{
|
|
128
|
+
fields: [ 'from.x', 'from.y' ],
|
|
129
|
+
validator: ( spec ) => spec.from.x !== spec.from.y,
|
|
130
|
+
error: 'from.x and from.y must be different fields'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
fields: [ 'logScale', 'scaleBy' ],
|
|
134
|
+
validator: ( spec ) => {
|
|
135
|
+
const hasLogScale = spec.logScale === true;
|
|
136
|
+
const hasScaleBy = spec.scaleBy !== undefined;
|
|
137
|
+
return !( hasLogScale && hasScaleBy );
|
|
138
|
+
},
|
|
139
|
+
error: 'logScale and scaleBy are mutually exclusive'
|
|
140
|
+
}
|
|
141
|
+
],
|
|
142
|
+
|
|
143
|
+
// How to build the spec from DSL params
|
|
144
|
+
// Pattern: NAME_X_Y_OUTPUTS_OPTIONS -> (name, x, y, stats, options)
|
|
145
|
+
buildSpec: ( name, x, y, stats, options ) => ( {
|
|
146
|
+
nodeType: NODE_TYPE,
|
|
147
|
+
name,
|
|
148
|
+
from: { x, y },
|
|
149
|
+
stats,
|
|
150
|
+
...options
|
|
151
|
+
} ),
|
|
152
|
+
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// nodes/ratio/publish-to.js
|
|
2
|
+
|
|
3
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
4
|
+
|
|
5
|
+
const publishTo = function ( state, msg ) {
|
|
6
|
+
if ( state.disable ) return;
|
|
7
|
+
// Propagate NaN if unhealthy
|
|
8
|
+
if ( state.inputValidationFailed ) {
|
|
9
|
+
publishNaN( state, msg );
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
msg[ state.stats.ratio.storeAs ] = state.ratio;
|
|
13
|
+
}; // publishTo()
|
|
14
|
+
|
|
15
|
+
export default publishTo;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// nodes/ratio/update.js
|
|
2
|
+
|
|
3
|
+
const LN_TO_DB = 20 / Math.LN10; // ≈ 8.6858896380650365
|
|
4
|
+
|
|
5
|
+
const update = function ( state, msg ) {
|
|
6
|
+
if ( state.disable || state.pause ) return state;
|
|
7
|
+
const xVal = msg[ state.x ];
|
|
8
|
+
// Reset on each update
|
|
9
|
+
state.inputValidationFailed = false;
|
|
10
|
+
// Handle faults gracefully: ensure their isolation
|
|
11
|
+
if ( !Number.isFinite( xVal ) ) {
|
|
12
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
13
|
+
state.inputValidationFailed = true;
|
|
14
|
+
return state;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const yVal = msg[ state.y ];
|
|
18
|
+
// Handle faults gracefully: ensure their isolation
|
|
19
|
+
if ( !Number.isFinite( yVal ) ) {
|
|
20
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
21
|
+
state.inputValidationFailed = true;
|
|
22
|
+
return state;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Protection against division by zero or very small numbers.
|
|
26
|
+
// Returns NaN to signal invalid computation - downstream nodes
|
|
27
|
+
// can detect and handle this appropriately.
|
|
28
|
+
if ( Math.abs( yVal ) < state.minY ) {
|
|
29
|
+
state.ratio = NaN; // Signal invalid ratio
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// For log scale, both values must be positive.
|
|
34
|
+
// Log of non-positive numbers is undefined.
|
|
35
|
+
if ( state.logScale && ( xVal <= 0 || yVal <= 0 ) ) {
|
|
36
|
+
state.ratio = NaN; // log of non-positive is undefined
|
|
37
|
+
return state;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
state.ratio = ( state.logScale ) ?
|
|
41
|
+
( Math.log( xVal ) - Math.log( yVal ) ) * LN_TO_DB :
|
|
42
|
+
( xVal / yVal ) * state.scaleBy;
|
|
43
|
+
|
|
44
|
+
return state;
|
|
45
|
+
}; // update()
|
|
46
|
+
|
|
47
|
+
export default update;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Sanitize Node - Universal data validation gate
|
|
3
|
+
*
|
|
4
|
+
* Validates data through configurable checks and maps invalid values to NaN.
|
|
5
|
+
* This provides a universal invalid marker that downstream nodes can detect
|
|
6
|
+
* with the fast check: value !== value
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Range validation with per-parameter bounds
|
|
10
|
+
* - Value list validation with allow/deny modes
|
|
11
|
+
* - Custom predicate validation
|
|
12
|
+
* - Works with both numeric and categorical data
|
|
13
|
+
*
|
|
14
|
+
* Configuration:
|
|
15
|
+
* - ranges: Object mapping field names to {min, max} bounds
|
|
16
|
+
* - valueList: Array of values to check against
|
|
17
|
+
* - containsValidValues: false = deny list (default), true = allow list
|
|
18
|
+
* - predicate: Optional function(value, msg) returning boolean
|
|
19
|
+
*
|
|
20
|
+
* Usage Examples:
|
|
21
|
+
*
|
|
22
|
+
* // Range validation (single field)
|
|
23
|
+
* .sanitize('temperature', {
|
|
24
|
+
* ranges: {
|
|
25
|
+
* temperature: { min: -40, max: 150 }
|
|
26
|
+
* }
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* // Multi-field with apply pattern
|
|
30
|
+
* .sanitize('sensors', ['temperature', 'pressure', 'flow'], ['sanitized'], {
|
|
31
|
+
* ranges: {
|
|
32
|
+
* temperature: { min: -40, max: 150 },
|
|
33
|
+
* pressure: { min: 0, max: 200 },
|
|
34
|
+
* flow: { min: 0, max: 1000 }
|
|
35
|
+
* }
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* // Deny list (blacklist) - common error codes
|
|
39
|
+
* .sanitize('signal', {
|
|
40
|
+
* valueList: [32767, -9999, "ERROR", "N/A"],
|
|
41
|
+
* containsValidValues: false
|
|
42
|
+
* })
|
|
43
|
+
*
|
|
44
|
+
* // Allow list (whitelist) - valid states only
|
|
45
|
+
* .sanitize('state', {
|
|
46
|
+
* valueList: ["RUNNING", "IDLE", "STOPPED"],
|
|
47
|
+
* containsValidValues: true
|
|
48
|
+
* })
|
|
49
|
+
*
|
|
50
|
+
* // Custom validation with predicate
|
|
51
|
+
* .sanitize('reading', {
|
|
52
|
+
* predicate: (value, msg) => value > msg.ambient
|
|
53
|
+
* })
|
|
54
|
+
*
|
|
55
|
+
* // Combined validation (checks in order: range, valueList, predicate)
|
|
56
|
+
* .sanitize('sensor', {
|
|
57
|
+
* ranges: {
|
|
58
|
+
* sensor: { min: 0, max: 100 }
|
|
59
|
+
* },
|
|
60
|
+
* valueList: [32767, -9999],
|
|
61
|
+
* containsValidValues: false,
|
|
62
|
+
* predicate: (value, msg) => value !== msg.lastValue
|
|
63
|
+
* })
|
|
64
|
+
*
|
|
65
|
+
* Failure Reporting:
|
|
66
|
+
* - failureReason: 'range' | 'valueList' | 'predicate' | null
|
|
67
|
+
* - failedValue: The value that failed validation
|
|
68
|
+
*
|
|
69
|
+
*/
|
|
70
|
+
|
|
71
|
+
export { default as init } from './init.js';
|
|
72
|
+
export { default as update } from './update.js';
|
|
73
|
+
export { default as reset } from './reset.js';
|
|
74
|
+
export { default as recompute } from './recompute.js';
|
|
75
|
+
export { default as publishTo } from './publish-to.js';
|
|
76
|
+
// Direct imports for common control functions
|
|
77
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
78
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
79
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
80
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
81
|
+
// Re-export everything from introspection.js
|
|
82
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { validateWithSchema } from '../../core/utils/validate/index.js';
|
|
2
|
+
import { getDSLMetadata, getNodeType, DEFAULT_OPTIONS } from './introspect.js';
|
|
3
|
+
import { resolveNestedObject, resolveArray } from '../../core/utils/options/resolve-field-keyed.js';
|
|
4
|
+
import { asTunable } from '../../core/tunable/index.js';
|
|
5
|
+
|
|
6
|
+
const init = function ( spec ) {
|
|
7
|
+
// Validate specification
|
|
8
|
+
const metadata = getDSLMetadata();
|
|
9
|
+
const validation = validateWithSchema(
|
|
10
|
+
{
|
|
11
|
+
...metadata.specSchema,
|
|
12
|
+
_crossFieldValidators: metadata.crossFieldValidators
|
|
13
|
+
},
|
|
14
|
+
spec,
|
|
15
|
+
'spec'
|
|
16
|
+
);
|
|
17
|
+
validation.throwIfInvalid( getNodeType() );
|
|
18
|
+
|
|
19
|
+
// Create state after validation passes
|
|
20
|
+
const state = Object.create( null );
|
|
21
|
+
|
|
22
|
+
// Ensure node is enabled by default
|
|
23
|
+
state.disable = false;
|
|
24
|
+
state.pause = false;
|
|
25
|
+
|
|
26
|
+
// Core configuration
|
|
27
|
+
state.x = spec.from.x;
|
|
28
|
+
state.stats = spec.stats;
|
|
29
|
+
|
|
30
|
+
// Pre-allocate failure reason strings (no allocations in hot path)
|
|
31
|
+
state.REASON_RANGE = 'range';
|
|
32
|
+
state.REASON_VALUE_LIST = 'valueList';
|
|
33
|
+
state.REASON_PREDICATE = 'predicate';
|
|
34
|
+
|
|
35
|
+
// Range configuration. Three shapes, all resolved by resolveNestedObject:
|
|
36
|
+
// Direct: { ranges: { min: -40, max: 85 } }
|
|
37
|
+
// Field-keyed: { ranges: { temp: { min: -40, max: 85 } } }
|
|
38
|
+
// Tunable: { ranges: lookupByField('shift', { day: {...}, night: {...} }, {...}) }
|
|
39
|
+
if ( typeof spec.ranges === 'function' ) {
|
|
40
|
+
// Dynamic ranges (tunable) - resolve at runtime
|
|
41
|
+
state.rangesFn = spec.ranges;
|
|
42
|
+
state.resolvedRangeSpec = null; // Not yet resolved
|
|
43
|
+
state.hasRange = true;
|
|
44
|
+
} else {
|
|
45
|
+
// Static ranges - resolve field-keying at init time
|
|
46
|
+
const rangeSpec = resolveNestedObject( spec.ranges, state.x, [ 'min', 'max' ] );
|
|
47
|
+
if ( rangeSpec ) {
|
|
48
|
+
state.rangesFn = asTunable( rangeSpec );
|
|
49
|
+
state.resolvedRangeSpec = rangeSpec; // Known at init
|
|
50
|
+
state.hasRange = true;
|
|
51
|
+
} else {
|
|
52
|
+
state.hasRange = false;
|
|
53
|
+
state.rangesFn = null;
|
|
54
|
+
state.resolvedRangeSpec = null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
state.tunableErrorLogged = false;
|
|
58
|
+
|
|
59
|
+
// Value list configuration (use Set for O(1) lookups)
|
|
60
|
+
// Supports both direct: { valueList: [1, 2, 3] }
|
|
61
|
+
// and field-keyed: { valueList: { temp: [1, 2], pressure: [3, 4] } }
|
|
62
|
+
const valueListSpec = resolveArray( spec.valueList, state.x );
|
|
63
|
+
const hasValueList = valueListSpec?.length > 0;
|
|
64
|
+
state.valueSet = hasValueList ? new Set( valueListSpec ) : null;
|
|
65
|
+
state.containsValidValues = spec.containsValidValues ?? DEFAULT_OPTIONS.containsValidValues;
|
|
66
|
+
|
|
67
|
+
// Custom predicate
|
|
68
|
+
state.predicate = spec.predicate || null;
|
|
69
|
+
// Log suppression: one log per error episode; reset on recovery.
|
|
70
|
+
state.predicateErrorLogged = false;
|
|
71
|
+
|
|
72
|
+
// Node metadata
|
|
73
|
+
state.nodeType = getNodeType();
|
|
74
|
+
|
|
75
|
+
// Initialize stats (null = no failure)
|
|
76
|
+
state.failureReason = null;
|
|
77
|
+
state.failedValue = null;
|
|
78
|
+
|
|
79
|
+
return state;
|
|
80
|
+
}; // init()
|
|
81
|
+
|
|
82
|
+
export default init;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
2
|
+
import { resolveNestedObject, resolveArray } from '../../core/utils/options/resolve-field-keyed.js';
|
|
3
|
+
|
|
4
|
+
/** Single source of truth for what this plugin can do */
|
|
5
|
+
const SUPPORTED_STATS = [
|
|
6
|
+
'failureReason',
|
|
7
|
+
'failedValue'
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
/** Human-readable descriptions (for introspection) */
|
|
11
|
+
const STAT_DESCRIPTIONS = {
|
|
12
|
+
failureReason: 'Type of validation failure: "range", "valueList", or "predicate" (null if valid)',
|
|
13
|
+
failedValue: 'The value that failed validation (null if valid)'
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
/** Control methods available for triggers */
|
|
17
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
18
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
19
|
+
enable: 'Enables node processing',
|
|
20
|
+
disable: 'Disables node processing',
|
|
21
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
22
|
+
unpause: 'Resumes node processing after pause'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** The type of this node */
|
|
26
|
+
const NODE_TYPE = 'Sanitize';
|
|
27
|
+
|
|
28
|
+
/** Node capabilities for documentation */
|
|
29
|
+
const CAPABILITIES = {
|
|
30
|
+
description: 'Universal data validation gate mapping invalid values to NaN',
|
|
31
|
+
features: [
|
|
32
|
+
'Range-based validation with per-parameter bounds',
|
|
33
|
+
'Value list validation with allow/deny modes',
|
|
34
|
+
'Custom predicate validation',
|
|
35
|
+
'Works with numeric and categorical data',
|
|
36
|
+
'Universal NaN invalid marker for downstream checking'
|
|
37
|
+
]
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Export functions for accessing metadata
|
|
41
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
42
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
43
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
44
|
+
export const getNodeType = () => NODE_TYPE;
|
|
45
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
46
|
+
|
|
47
|
+
// Spec default values
|
|
48
|
+
export const DEFAULT_OPTIONS = {
|
|
49
|
+
valueList: [],
|
|
50
|
+
containsValidValues: false // false = deny list, true = allow list
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/** Parameters that support tunable (dynamic) values */
|
|
54
|
+
export const TUNABLE_PARAMS = [ 'ranges' ];
|
|
55
|
+
|
|
56
|
+
// DSL Metadata for transpilation
|
|
57
|
+
const DSL_METADATA = {
|
|
58
|
+
specSchema: {
|
|
59
|
+
nodeType: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
required: true,
|
|
62
|
+
value: NODE_TYPE
|
|
63
|
+
},
|
|
64
|
+
name: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
required: true,
|
|
67
|
+
validator: validators.identifier,
|
|
68
|
+
error: 'Name must be a valid identifier'
|
|
69
|
+
},
|
|
70
|
+
from: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
required: true,
|
|
73
|
+
properties: {
|
|
74
|
+
x: {
|
|
75
|
+
type: 'string',
|
|
76
|
+
required: true,
|
|
77
|
+
validator: validators.noSpaces,
|
|
78
|
+
error: 'Field name cannot contain spaces'
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
},
|
|
82
|
+
stats: {
|
|
83
|
+
type: 'object',
|
|
84
|
+
required: true,
|
|
85
|
+
minProperties: 1,
|
|
86
|
+
propertyNames: SUPPORTED_STATS,
|
|
87
|
+
propertySchema: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
properties: {
|
|
90
|
+
storeAs: {
|
|
91
|
+
type: 'string',
|
|
92
|
+
required: true,
|
|
93
|
+
validator: validators.identifier,
|
|
94
|
+
error: 'storeAs must be a valid identifier'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
ranges: {
|
|
100
|
+
type: 'nestedObjectOrFunctionOrFieldKeyed',
|
|
101
|
+
required: false,
|
|
102
|
+
// Accepts three shapes the runtime resolver already handles:
|
|
103
|
+
// direct { min: 0, max: 100 }
|
|
104
|
+
// field-keyed { temp: { min: -40, max: 85 }, ... }
|
|
105
|
+
// tunable (msg) => ({ min: msg.lo, max: msg.hi })
|
|
106
|
+
// The inner shape below (both bounds required, numeric) is read by
|
|
107
|
+
// the type validator; min <= max is checked in a cross-field rule.
|
|
108
|
+
properties: {
|
|
109
|
+
min: { type: 'number', required: true },
|
|
110
|
+
max: { type: 'number', required: true }
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
valueList: {
|
|
114
|
+
type: 'arrayOrFieldKeyed',
|
|
115
|
+
required: false,
|
|
116
|
+
default: DEFAULT_OPTIONS.valueList,
|
|
117
|
+
itemSchema: {
|
|
118
|
+
type: 'any' // Can be numbers, strings, booleans, etc.
|
|
119
|
+
},
|
|
120
|
+
// eslint-disable-next-line no-self-compare
|
|
121
|
+
validator: ( arr ) => !Array.isArray( arr ) || arr.every( ( x ) => x === x ) // reject NaN
|
|
122
|
+
},
|
|
123
|
+
containsValidValues: {
|
|
124
|
+
type: 'boolean',
|
|
125
|
+
required: false,
|
|
126
|
+
default: DEFAULT_OPTIONS.containsValidValues
|
|
127
|
+
},
|
|
128
|
+
predicate: {
|
|
129
|
+
type: 'function',
|
|
130
|
+
required: false,
|
|
131
|
+
arity: 2,
|
|
132
|
+
error: 'Predicate must accept (value, msg) parameters'
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
crossFieldValidators: [
|
|
137
|
+
{
|
|
138
|
+
fields: [ 'ranges', 'valueList', 'predicate' ],
|
|
139
|
+
validator: ( spec ) => {
|
|
140
|
+
// ranges can be a function (tunable) or object (direct or field-keyed)
|
|
141
|
+
const hasRanges = typeof spec.ranges === 'function' ||
|
|
142
|
+
( spec.ranges && Object.keys( spec.ranges ).length > 0 );
|
|
143
|
+
// Resolve valueList for the node's field so a direct array and a
|
|
144
|
+
// field-keyed map both count as "a method was provided".
|
|
145
|
+
const valueList = resolveArray( spec.valueList, spec.from?.x );
|
|
146
|
+
const hasValueList = Array.isArray( valueList ) && valueList.length > 0;
|
|
147
|
+
const hasPredicate = typeof spec.predicate === 'function';
|
|
148
|
+
return hasRanges || hasValueList || hasPredicate;
|
|
149
|
+
},
|
|
150
|
+
error: 'Must provide at least one validation method: ranges, valueList, or predicate'
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
fields: [ 'from.x', 'ranges' ],
|
|
154
|
+
validator: ( spec ) => {
|
|
155
|
+
// If ranges is a function, validation deferred to runtime
|
|
156
|
+
if ( typeof spec.ranges === 'function' ) return true;
|
|
157
|
+
// No ranges specified — nothing to check here
|
|
158
|
+
if ( !spec.ranges || Object.keys( spec.ranges ).length === 0 ) {
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
// A direct range resolves for this field; a field-keyed map must
|
|
162
|
+
// carry an entry for it. Use the same resolver the runtime uses
|
|
163
|
+
// so validation and runtime agree on what "resolves" means.
|
|
164
|
+
return resolveNestedObject( spec.ranges, spec.from.x, [ 'min', 'max' ] ) !== undefined;
|
|
165
|
+
},
|
|
166
|
+
error: 'Ranges must be a direct { min, max } or a per-field map that includes the field this node reads'
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
fields: [ 'ranges' ],
|
|
170
|
+
validator: ( spec ) => {
|
|
171
|
+
// If ranges is a function, validation deferred to runtime
|
|
172
|
+
if ( typeof spec.ranges === 'function' ) return true;
|
|
173
|
+
if ( !spec.ranges ) return true;
|
|
174
|
+
// A direct range carries min/max itself; a field-keyed map carries
|
|
175
|
+
// one range per field. Check min <= max on whichever shape was given.
|
|
176
|
+
const isDirect = ( 'min' in spec.ranges ) || ( 'max' in spec.ranges );
|
|
177
|
+
const ranges = isDirect ? [ spec.ranges ] : Object.values( spec.ranges );
|
|
178
|
+
return ranges.every( ( range ) => range.min <= range.max );
|
|
179
|
+
},
|
|
180
|
+
error: 'Range min must be less than or equal to max'
|
|
181
|
+
}
|
|
182
|
+
],
|
|
183
|
+
|
|
184
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
185
|
+
nodeType: NODE_TYPE,
|
|
186
|
+
name,
|
|
187
|
+
from: { x },
|
|
188
|
+
stats,
|
|
189
|
+
...options
|
|
190
|
+
} ),
|
|
191
|
+
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const publishTo = function ( state, msg ) {
|
|
2
|
+
if ( state.disable ) return;
|
|
3
|
+
|
|
4
|
+
// Publish failure information if present
|
|
5
|
+
if ( state.failureReason ) {
|
|
6
|
+
if ( state.stats.failureReason ) {
|
|
7
|
+
msg[ state.stats.failureReason.storeAs ] = state.failureReason;
|
|
8
|
+
}
|
|
9
|
+
if ( state.stats.failedValue ) {
|
|
10
|
+
msg[ state.stats.failedValue.storeAs ] = state.failedValue;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}; // publishTo()
|
|
14
|
+
|
|
15
|
+
export default publishTo;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const reset = function ( state ) {
|
|
2
|
+
// Stateless validation node - nothing to reset
|
|
3
|
+
// Failure status is per-message, not accumulated
|
|
4
|
+
|
|
5
|
+
state.failureReason = null;
|
|
6
|
+
state.failedValue = null;
|
|
7
|
+
// Clear error suppression so next error episode is logged
|
|
8
|
+
state.predicateErrorLogged = false;
|
|
9
|
+
state.tunableErrorLogged = false;
|
|
10
|
+
|
|
11
|
+
return state;
|
|
12
|
+
}; // reset()
|
|
13
|
+
|
|
14
|
+
export default reset;
|