@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,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Reset function for emitIf node
|
|
3
|
+
*
|
|
4
|
+
* Resets statistics and state tracking.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const reset = function ( state ) {
|
|
8
|
+
// Reset statistics
|
|
9
|
+
state.emissionCount = 0;
|
|
10
|
+
state.passCount = 0;
|
|
11
|
+
state.lastEmissionTime = null;
|
|
12
|
+
state.emissionErrors = 0;
|
|
13
|
+
state.lastEmissionError = null;
|
|
14
|
+
state.lastEmissionErrorCode = null;
|
|
15
|
+
state.firstEmissionError = null;
|
|
16
|
+
state.firstEmissionErrorCode = null;
|
|
17
|
+
|
|
18
|
+
// Reset state tracking
|
|
19
|
+
state.inErrorState = false;
|
|
20
|
+
// Clear error suppression so next error episode is logged
|
|
21
|
+
state.predicateErrorLogged = false;
|
|
22
|
+
state.emitErrorLogged = false;
|
|
23
|
+
|
|
24
|
+
return true;
|
|
25
|
+
}; // reset()
|
|
26
|
+
|
|
27
|
+
export default reset;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Update function for emitIf node
|
|
3
|
+
*
|
|
4
|
+
* Hot path — evaluates user-supplied predicate and emits to external
|
|
5
|
+
* systems when true. Predicate errors are caught and tracked per-episode.
|
|
6
|
+
*
|
|
7
|
+
* Publishes unconditionally — no connectivity pre-check (ADR-018). The
|
|
8
|
+
* old isConnected() guard skipped publishes during MQTT disconnects, which
|
|
9
|
+
* kept messages OUT of the emitter's buffer: the exact loss buffering
|
|
10
|
+
* exists to prevent. (ADR-021 later replaced the on-disk offline store
|
|
11
|
+
* with an in-process buffer; the invariant is unchanged.) The emitter's
|
|
12
|
+
* { ok } return is the only truth
|
|
13
|
+
* the node consumes; a failure opens a loud episode (mirroring persistIf —
|
|
14
|
+
* the two output gates share one failure model).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { assertAnnotateReturn, emitStatusSignal, recordEmissionFailure, MALFORMED_RESULT_ERROR } from './helpers.js';
|
|
18
|
+
|
|
19
|
+
const update = function ( state, msg ) {
|
|
20
|
+
// Always increment pass count for observability
|
|
21
|
+
state.passCount += 1;
|
|
22
|
+
|
|
23
|
+
// Early exit if emitter not available (mirrors persistIf's storage guard)
|
|
24
|
+
if ( !state.emitter ) {
|
|
25
|
+
return state;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Evaluate predicate, then shape the payload when emitting. Annotate
|
|
29
|
+
// mirrors persistIf: both run inside one try, so predicate and
|
|
30
|
+
// annotate share the same error episode (tracking, log suppression,
|
|
31
|
+
// recovery).
|
|
32
|
+
try {
|
|
33
|
+
const shouldEmit = state.predicate( msg );
|
|
34
|
+
let data = msg;
|
|
35
|
+
if ( shouldEmit && state.annotate ) {
|
|
36
|
+
data = state.annotate( msg );
|
|
37
|
+
// A flow-authoring bug (non-object return) throws here, into
|
|
38
|
+
// this node's error episode — never published as-is (mirrors
|
|
39
|
+
// persistIf).
|
|
40
|
+
assertAnnotateReturn( data );
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if ( shouldEmit ) {
|
|
44
|
+
// Per the ADR-018 sink return contract, the emitter answers
|
|
45
|
+
// { ok: true } on success or
|
|
46
|
+
// { ok: false, error: { code, message } } on failure.
|
|
47
|
+
const result = state.emitter.publishNow( state.topic, data );
|
|
48
|
+
|
|
49
|
+
if ( result && result.ok ) {
|
|
50
|
+
state.emissionCount += 1;
|
|
51
|
+
state.lastEmissionTime = Date.now();
|
|
52
|
+
// Recovery: a successful publish closes the failure episode.
|
|
53
|
+
if ( state.emitErrorLogged ) state.emitErrorLogged = false;
|
|
54
|
+
} else {
|
|
55
|
+
// A malformed result (no object at all, or ok:false with no
|
|
56
|
+
// error) counts as a failure with the static fallback error,
|
|
57
|
+
// so a non-conforming adapter can neither fail silently
|
|
58
|
+
// nor throw out of the hot path.
|
|
59
|
+
recordEmissionFailure( state, ( result && result.error ) || MALFORMED_RESULT_ERROR );
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Clear error state on successful evaluation
|
|
64
|
+
if ( state.inErrorState ) {
|
|
65
|
+
state.inErrorState = false;
|
|
66
|
+
emitStatusSignal( state, false, 'recovered' );
|
|
67
|
+
}
|
|
68
|
+
// Recovery: clear log-suppression flag on success
|
|
69
|
+
if ( state.predicateErrorLogged ) state.predicateErrorLogged = false;
|
|
70
|
+
} catch ( error ) {
|
|
71
|
+
// Handle predicate errors (semantic — emits status signal)
|
|
72
|
+
if ( !state.inErrorState ) {
|
|
73
|
+
state.inErrorState = true;
|
|
74
|
+
emitStatusSignal( state, true, error.message );
|
|
75
|
+
}
|
|
76
|
+
// Log first error per episode; suppress subsequent until recovery
|
|
77
|
+
if ( !state.predicateErrorLogged ) {
|
|
78
|
+
state.predicateErrorLogged = true;
|
|
79
|
+
console.error( `WinkComposer/emitIf: predicate threw exception: ${error.message}` );
|
|
80
|
+
}
|
|
81
|
+
state.emissionErrors += 1;
|
|
82
|
+
state.lastEmissionError = error.message;
|
|
83
|
+
// Predicate exceptions are emit-if-internal, not part of the
|
|
84
|
+
// ADR-018 adapter err.code vocabulary. Clear the code so the two
|
|
85
|
+
// fields stay in sync (a stale code from a prior publish failure
|
|
86
|
+
// would mislead readers).
|
|
87
|
+
state.lastEmissionErrorCode = null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return state;
|
|
91
|
+
}; // update()
|
|
92
|
+
|
|
93
|
+
export default update;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel module for the es-correlation node. Re-exports lifecycle
|
|
3
|
+
* methods (init, update, reset, recompute, publishTo), shared control signals
|
|
4
|
+
* (disable, enable, pause, unpause), and all introspection metadata.
|
|
5
|
+
*/
|
|
6
|
+
// nodes/es-correlation/index.js
|
|
7
|
+
|
|
8
|
+
export { default as init } from './init.js';
|
|
9
|
+
export { default as update } from './update.js';
|
|
10
|
+
export { default as reset } from './reset.js';
|
|
11
|
+
export { default as recompute } from './recompute.js';
|
|
12
|
+
export { default as publishTo } from './publish-to.js';
|
|
13
|
+
// Direct imports for common control functions
|
|
14
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
15
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
16
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
17
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
18
|
+
// Re-export everything from introspection.js
|
|
19
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Initializes es-correlation node state from a validated spec.
|
|
3
|
+
* Derives the smoothing factor (alpha) from the configured half-life,
|
|
4
|
+
* pre-allocates all state properties on a prototype-free object — no
|
|
5
|
+
* allocations occur on the hot path.
|
|
6
|
+
*/
|
|
7
|
+
// nodes/es-correlation/init.js
|
|
8
|
+
|
|
9
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
10
|
+
import * as introspect from './introspect.js';
|
|
11
|
+
import { halfLifeToAlpha, halfLifeToWarmupSamples } from '../../core/utils/half-life/index.js';
|
|
12
|
+
|
|
13
|
+
const fisherZCap = 0.9999;
|
|
14
|
+
|
|
15
|
+
const init = function ( spec ) {
|
|
16
|
+
// Validate against (updated) schema that uses halfLife
|
|
17
|
+
validateSpec( spec, introspect );
|
|
18
|
+
|
|
19
|
+
// Create state after validation passes
|
|
20
|
+
const state = Object.create( null );
|
|
21
|
+
// Standard input parameter health flag during update: false means healthy
|
|
22
|
+
state.inputValidationFailed = false;
|
|
23
|
+
// Ensure node is enabled by default
|
|
24
|
+
state.disable = false;
|
|
25
|
+
state.pause = false;
|
|
26
|
+
|
|
27
|
+
// Required fields for correlation computation
|
|
28
|
+
state.x = spec.from.x;
|
|
29
|
+
state.y = spec.from.y;
|
|
30
|
+
|
|
31
|
+
// Config copy for output field mapping
|
|
32
|
+
state.stats = spec.stats;
|
|
33
|
+
|
|
34
|
+
// Half-life based configuration (samples). If not provided, fall back to introspect defaults.
|
|
35
|
+
state.halfLife = spec.halfLife ?? introspect.DEFAULT_OPTIONS.halfLife;
|
|
36
|
+
|
|
37
|
+
// Derive alpha from half-life for hot-path math
|
|
38
|
+
state.alpha = halfLifeToAlpha( state.halfLife );
|
|
39
|
+
|
|
40
|
+
// Variance floor (retain existing default behavior if unspecified)
|
|
41
|
+
state.minVariance = spec.minVariance ?? introspect.DEFAULT_OPTIONS.minVariance;
|
|
42
|
+
|
|
43
|
+
// Warmup: if not provided, compute minSamples to reach ~95% settled response
|
|
44
|
+
// Using HL→samples mapping keeps early estimates reliable without manual tuning
|
|
45
|
+
state.minSamples = spec.minSamples ?? halfLifeToWarmupSamples( state.halfLife, 0.85 );
|
|
46
|
+
|
|
47
|
+
// Optional Fisher Z transformation enablement; keep existing cap behavior
|
|
48
|
+
state.fisherZCap = ( spec.fisherZT === true ) ? fisherZCap : 1;
|
|
49
|
+
|
|
50
|
+
// Core state variables for numerically stable ES computation.
|
|
51
|
+
// Using Welford-style incremental updates with exponential weighting.
|
|
52
|
+
// Means will be initialized with first observation (x₀, y₀) in update() for
|
|
53
|
+
// faster convergence and more natural variance evolution.
|
|
54
|
+
state.meanX = 0;
|
|
55
|
+
state.meanY = 0;
|
|
56
|
+
state.varianceX = 0;
|
|
57
|
+
state.varianceY = 0;
|
|
58
|
+
state.covariance = 0;
|
|
59
|
+
|
|
60
|
+
// Output values.
|
|
61
|
+
state.correlation = 0;
|
|
62
|
+
state.r2 = 0; // Coefficient of determination (correlation squared)
|
|
63
|
+
state.fisherZT = 0;
|
|
64
|
+
|
|
65
|
+
// Track sample count for initialization phase.
|
|
66
|
+
state.sampleCount = 0;
|
|
67
|
+
|
|
68
|
+
state.nodeType = introspect.getNodeType();
|
|
69
|
+
|
|
70
|
+
return state;
|
|
71
|
+
}; // init()
|
|
72
|
+
|
|
73
|
+
export default init;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Single source of truth for the es-correlation node's supported
|
|
3
|
+
* stats, control methods, capabilities, default options, and DSL schema.
|
|
4
|
+
* All getters return defensive copies to prevent caller mutation.
|
|
5
|
+
*/
|
|
6
|
+
// nodes/es-correlation/introspect.js
|
|
7
|
+
|
|
8
|
+
import { validators, composerValidators } from '../../core/utils/validate/index.js';
|
|
9
|
+
|
|
10
|
+
/** Single source of truth for what this plugin can do */
|
|
11
|
+
const SUPPORTED_STATS = [
|
|
12
|
+
'correlation',
|
|
13
|
+
'covariance',
|
|
14
|
+
'r2',
|
|
15
|
+
'fisherZT'
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/** Human-readable descriptions (for introspection) */
|
|
19
|
+
const STAT_DESCRIPTIONS = {
|
|
20
|
+
correlation: 'Pearson correlation coefficient between x and y using EWMA, range [-1, 1]',
|
|
21
|
+
covariance: 'Covariance between x and y using EWMA',
|
|
22
|
+
r2: 'Coefficient of determination (r-squared), proportion of variance explained, range [0, 1]',
|
|
23
|
+
fisherZT: 'Fisher Z transformation of correlation to normalize it'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/** Control methods available for triggers */
|
|
27
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
28
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
29
|
+
enable: 'Enables node processing',
|
|
30
|
+
disable: 'Disables node processing',
|
|
31
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
32
|
+
unpause: 'Resumes node processing after pause'
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** The type of this node */
|
|
36
|
+
const NODE_TYPE = 'ES Correlation';
|
|
37
|
+
|
|
38
|
+
/** Node capabilities for documentation */
|
|
39
|
+
const CAPABILITIES = {
|
|
40
|
+
description: 'Computes exponentially smoothed correlation and covariance between two variables',
|
|
41
|
+
features: [
|
|
42
|
+
'Numerically stable Welford-style algorithm',
|
|
43
|
+
'Exponential weighting for dynamic tracking',
|
|
44
|
+
'Handles near-zero variance gracefully',
|
|
45
|
+
'Supports correlation, covariance, and r² output',
|
|
46
|
+
'Half-life (samples) supported for intuitive tuning'
|
|
47
|
+
]
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Attach static metadata once, from our constants by making a shallow copy.
|
|
51
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
52
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
53
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
54
|
+
export const getNodeType = () => NODE_TYPE;
|
|
55
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
56
|
+
|
|
57
|
+
export const DEFAULT_OPTIONS = {
|
|
58
|
+
// halfLife roughly equivalent to alpha = 0.05
|
|
59
|
+
halfLife: 13.5,
|
|
60
|
+
minVariance: 1e-12
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// DSL Metadata for transpilation
|
|
64
|
+
const DSL_METADATA = {
|
|
65
|
+
// Full spec schema for validation
|
|
66
|
+
specSchema: {
|
|
67
|
+
nodeType: {
|
|
68
|
+
type: 'string',
|
|
69
|
+
required: true,
|
|
70
|
+
value: NODE_TYPE
|
|
71
|
+
},
|
|
72
|
+
name: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
required: true,
|
|
75
|
+
validator: validators.identifier,
|
|
76
|
+
error: 'Name must be a valid identifier'
|
|
77
|
+
},
|
|
78
|
+
from: {
|
|
79
|
+
type: 'object',
|
|
80
|
+
required: true,
|
|
81
|
+
properties: {
|
|
82
|
+
x: {
|
|
83
|
+
type: 'string',
|
|
84
|
+
required: true,
|
|
85
|
+
validator: validators.noSpaces,
|
|
86
|
+
error: 'Field name cannot contain spaces'
|
|
87
|
+
},
|
|
88
|
+
y: {
|
|
89
|
+
type: 'string',
|
|
90
|
+
required: true,
|
|
91
|
+
validator: validators.noSpaces,
|
|
92
|
+
error: 'Field name cannot contain spaces'
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
halfLife: {
|
|
97
|
+
type: 'number',
|
|
98
|
+
required: false,
|
|
99
|
+
default: DEFAULT_OPTIONS.halfLife,
|
|
100
|
+
validator: composerValidators.halfLife,
|
|
101
|
+
error: 'halfLife must be > 0 and < 999999'
|
|
102
|
+
},
|
|
103
|
+
minVariance: {
|
|
104
|
+
type: 'number',
|
|
105
|
+
required: false,
|
|
106
|
+
default: DEFAULT_OPTIONS.minVariance,
|
|
107
|
+
min: 0,
|
|
108
|
+
validator: validators.positive,
|
|
109
|
+
error: 'Minimum variance must be positive'
|
|
110
|
+
},
|
|
111
|
+
minSamples: {
|
|
112
|
+
type: 'number',
|
|
113
|
+
required: false,
|
|
114
|
+
default: 10,
|
|
115
|
+
integer: true,
|
|
116
|
+
min: 1,
|
|
117
|
+
validator: validators.positiveInteger,
|
|
118
|
+
error: 'Minimum samples must be a positive integer'
|
|
119
|
+
},
|
|
120
|
+
fisherZT: {
|
|
121
|
+
type: 'boolean',
|
|
122
|
+
required: false,
|
|
123
|
+
default: false
|
|
124
|
+
},
|
|
125
|
+
stats: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
required: true,
|
|
128
|
+
minProperties: 1,
|
|
129
|
+
propertyNames: SUPPORTED_STATS,
|
|
130
|
+
propertySchema: {
|
|
131
|
+
type: 'object',
|
|
132
|
+
properties: {
|
|
133
|
+
storeAs: {
|
|
134
|
+
type: 'string',
|
|
135
|
+
required: true,
|
|
136
|
+
validator: validators.identifier,
|
|
137
|
+
error: 'storeAs must be a valid identifier'
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
// Cross-field validators
|
|
145
|
+
crossFieldValidators: [
|
|
146
|
+
{
|
|
147
|
+
fields: [ 'from.x', 'from.y' ],
|
|
148
|
+
validator: ( spec ) => spec.from.x !== spec.from.y,
|
|
149
|
+
error: 'from.x and from.y must be different fields'
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
fields: [ 'fisherZT', 'stats.fisherZT' ],
|
|
153
|
+
validator: ( spec ) => {
|
|
154
|
+
// If fisherZT stat is requested, fisherZT must be enabled
|
|
155
|
+
if ( spec.stats && spec.stats.fisherZT ) {
|
|
156
|
+
return spec.fisherZT === true;
|
|
157
|
+
}
|
|
158
|
+
return true; // No fisherZT stat requested, so no constraint
|
|
159
|
+
},
|
|
160
|
+
error: 'stats.fisherZT requires fisherZT to be enabled'
|
|
161
|
+
}
|
|
162
|
+
],
|
|
163
|
+
|
|
164
|
+
// Build spec from DSL parameters
|
|
165
|
+
// Pattern: NAME_X_Y_OUTPUTS_OPTIONS -> (name, x, y, stats, options)
|
|
166
|
+
buildSpec: ( name, x, y, stats, options ) => ( {
|
|
167
|
+
nodeType: NODE_TYPE,
|
|
168
|
+
name,
|
|
169
|
+
from: { x, y },
|
|
170
|
+
stats,
|
|
171
|
+
...options
|
|
172
|
+
} ),
|
|
173
|
+
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Copies the current correlation, covariance, r², and/or Fisher Z
|
|
3
|
+
* estimates onto the message for downstream consumption. Propagates NaN when
|
|
4
|
+
* input validation has failed; gates output until minSamples is reached.
|
|
5
|
+
*/
|
|
6
|
+
// nodes/es-correlation/publish-to.js
|
|
7
|
+
|
|
8
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
9
|
+
|
|
10
|
+
const publishTo = function ( state, msg ) {
|
|
11
|
+
if ( state.disable ) return;
|
|
12
|
+
// Propagate NaN if unhealthy
|
|
13
|
+
if ( state.inputValidationFailed ) {
|
|
14
|
+
publishNaN( state, msg );
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Publish only after minimum samples collected to avoid unreliable early estimates.
|
|
18
|
+
// Align with update/recompute behavior by allowing publish at minSamples as well.
|
|
19
|
+
if ( state.sampleCount < state.minSamples ) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Iterate only over configured stats
|
|
24
|
+
const stats = state.stats;
|
|
25
|
+
for ( const statName in stats ) {
|
|
26
|
+
if ( Object.prototype.hasOwnProperty.call( stats, statName ) ) {
|
|
27
|
+
msg[ stats[ statName ].storeAs ] = state[ statName ];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}; // publishTo()
|
|
31
|
+
|
|
32
|
+
export default publishTo;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Numerical stability handler for the es-correlation node.
|
|
3
|
+
* Floors negative variances (floating-point guard), recomputes correlation
|
|
4
|
+
* from current accumulated state, and clamps the result to [-1, 1].
|
|
5
|
+
*/
|
|
6
|
+
// nodes/es-correlation/recompute.js
|
|
7
|
+
|
|
8
|
+
const recompute = function ( state ) {
|
|
9
|
+
// It doesn't store historical data, so traditional recomputation isn't possible.
|
|
10
|
+
// Instead, we perform numerical stability checks and adjustments.
|
|
11
|
+
|
|
12
|
+
// Check for numerical issues in variances.
|
|
13
|
+
// Variance should never be negative due to our update formula,
|
|
14
|
+
// but floating point errors could theoretically cause issues.
|
|
15
|
+
if ( state.varianceX < 0 ) {
|
|
16
|
+
state.varianceX = 0;
|
|
17
|
+
}
|
|
18
|
+
if ( state.varianceY < 0 ) {
|
|
19
|
+
state.varianceY = 0;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Recompute correlation from current statistics to ensure consistency.
|
|
23
|
+
if ( state.sampleCount >= state.minSamples ) {
|
|
24
|
+
const stdX = Math.sqrt( Math.max( state.varianceX, state.minVariance ) );
|
|
25
|
+
const stdY = Math.sqrt( Math.max( state.varianceY, state.minVariance ) );
|
|
26
|
+
|
|
27
|
+
state.correlation = state.covariance / ( stdX * stdY );
|
|
28
|
+
|
|
29
|
+
// Ensure correlation is in valid range.
|
|
30
|
+
if ( state.correlation > 1 ) {
|
|
31
|
+
state.correlation = 1;
|
|
32
|
+
} else if ( state.correlation < -1 ) {
|
|
33
|
+
state.correlation = -1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check for NaN (could occur if both variances are zero despite protection).
|
|
37
|
+
if ( Number.isNaN( state.correlation ) ) {
|
|
38
|
+
state.correlation = 0;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Keep derived stats consistent with adjusted correlation.
|
|
42
|
+
if ( state.stats && state.stats.r2 ) {
|
|
43
|
+
state.r2 = state.correlation * state.correlation;
|
|
44
|
+
}
|
|
45
|
+
if ( ( state.fisherZCap < 1 ) && state.stats && state.stats.fisherZT ) {
|
|
46
|
+
const r = state.correlation;
|
|
47
|
+
const capped = ( r > state.fisherZCap ) ? state.fisherZCap :
|
|
48
|
+
( r < -state.fisherZCap ? -state.fisherZCap : r );
|
|
49
|
+
state.fisherZT = 0.5 * Math.log( ( 1 + capped ) / ( 1 - capped ) );
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return true;
|
|
54
|
+
}; // recompute()
|
|
55
|
+
|
|
56
|
+
export default recompute;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resets all statistical accumulators (means, variances,
|
|
3
|
+
* covariance) and output values (correlation, r², fisherZT) to their
|
|
4
|
+
* initial state, allowing the node to restart computation from scratch.
|
|
5
|
+
*/
|
|
6
|
+
// nodes/es-correlation/reset.js
|
|
7
|
+
|
|
8
|
+
const reset = function ( state ) {
|
|
9
|
+
// Reset all statistical accumulators to initial values.
|
|
10
|
+
state.meanX = 0;
|
|
11
|
+
state.meanY = 0;
|
|
12
|
+
state.varianceX = 0;
|
|
13
|
+
state.varianceY = 0;
|
|
14
|
+
state.covariance = 0;
|
|
15
|
+
|
|
16
|
+
// Reset output values.
|
|
17
|
+
state.correlation = 0;
|
|
18
|
+
state.r2 = 0;
|
|
19
|
+
state.fisherZT = 0;
|
|
20
|
+
|
|
21
|
+
// Reset sample counter.
|
|
22
|
+
state.sampleCount = 0;
|
|
23
|
+
|
|
24
|
+
return true;
|
|
25
|
+
}; // reset()
|
|
26
|
+
|
|
27
|
+
export default reset;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Hot-path exponentially weighted moving correlation computation.
|
|
3
|
+
* Uses a Welford-style incremental algorithm with exponential decay on means,
|
|
4
|
+
* variance, and covariance. Optionally computes r² and Fisher Z transform.
|
|
5
|
+
* Zero allocations; all reads/writes via the pre-allocated state object.
|
|
6
|
+
*/
|
|
7
|
+
// nodes/es-correlation/update.js
|
|
8
|
+
|
|
9
|
+
const update = function ( state, msg ) {
|
|
10
|
+
if ( state.disable || state.pause ) return state;
|
|
11
|
+
const xVal = msg[ state.x ];
|
|
12
|
+
// Reset on each update
|
|
13
|
+
state.inputValidationFailed = false;
|
|
14
|
+
// Handle faults gracefully: ensure their isolation
|
|
15
|
+
if ( !Number.isFinite( xVal ) ) {
|
|
16
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
17
|
+
state.inputValidationFailed = true;
|
|
18
|
+
return state;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const yVal = msg[ state.y ];
|
|
22
|
+
// Handle faults gracefully: ensure their isolation
|
|
23
|
+
if ( !Number.isFinite( yVal ) ) {
|
|
24
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
25
|
+
state.inputValidationFailed = true;
|
|
26
|
+
return state;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Special handling for first observation:
|
|
30
|
+
// Initialize means with the first sample for faster convergence
|
|
31
|
+
// and more natural variance evolution; no variance/covariance yet.
|
|
32
|
+
if ( state.sampleCount === 0 ) {
|
|
33
|
+
state.meanX = xVal;
|
|
34
|
+
state.meanY = yVal;
|
|
35
|
+
state.sampleCount = 1; // count the first sample
|
|
36
|
+
return state;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Increment sample count for subsequent observations.
|
|
40
|
+
state.sampleCount += 1;
|
|
41
|
+
|
|
42
|
+
// Compute deltas BEFORE updating means (critical for numerical stability).
|
|
43
|
+
const deltaX = xVal - state.meanX;
|
|
44
|
+
const deltaY = yVal - state.meanY;
|
|
45
|
+
|
|
46
|
+
// Exponentially smoothed update for means.
|
|
47
|
+
state.meanX += state.alpha * deltaX;
|
|
48
|
+
state.meanY += state.alpha * deltaY;
|
|
49
|
+
|
|
50
|
+
// Numerically stable variance and covariance updates.
|
|
51
|
+
// Note: We use the NEW mean for the second delta computation.
|
|
52
|
+
|
|
53
|
+
// Update covariance using the stable formula.
|
|
54
|
+
// cov += α * δx * (y - mean_y_new)
|
|
55
|
+
state.covariance += state.alpha * ( ( deltaX * ( yVal - state.meanY ) ) - state.covariance );
|
|
56
|
+
|
|
57
|
+
// If neither correlation nor r2 is requested, we can skip the
|
|
58
|
+
// variance/correlation path (keeps hot path lean). r2 implies
|
|
59
|
+
// correlation at compute-time even if not explicitly requested.
|
|
60
|
+
if ( ( state.stats.correlation === undefined ) && ( state.stats.r2 === undefined ) ) {
|
|
61
|
+
return state;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Update variances using the stable formula.
|
|
65
|
+
// var_x += α * δx * (x - mean_x_new)
|
|
66
|
+
state.varianceX += state.alpha * ( ( deltaX * ( xVal - state.meanX ) ) - state.varianceX );
|
|
67
|
+
state.varianceY += state.alpha * ( ( deltaY * ( yVal - state.meanY ) ) - state.varianceY );
|
|
68
|
+
|
|
69
|
+
// Compute correlation only after minimum samples collected.
|
|
70
|
+
if ( state.sampleCount >= state.minSamples ) {
|
|
71
|
+
// Compute standard deviations with minimum variance protection.
|
|
72
|
+
const stdX = Math.sqrt( Math.max( state.varianceX, state.minVariance ) );
|
|
73
|
+
const stdY = Math.sqrt( Math.max( state.varianceY, state.minVariance ) );
|
|
74
|
+
|
|
75
|
+
// Compute correlation coefficient.
|
|
76
|
+
// The max() ensures we never divide by zero.
|
|
77
|
+
state.correlation = state.covariance / ( stdX * stdY );
|
|
78
|
+
|
|
79
|
+
// Clamp correlation to [-1, 1] to handle numerical errors.
|
|
80
|
+
// Due to floating point arithmetic, we might get values like 1.0000000002.
|
|
81
|
+
if ( state.correlation >= 1 ) {
|
|
82
|
+
// Note: fisherZCap currently couples clamping and Z-publish enabling.
|
|
83
|
+
// We will decouple this in a subsequent Fisher-Z clarity pass.
|
|
84
|
+
state.correlation = state.fisherZCap;
|
|
85
|
+
} else if ( state.correlation <= -1 ) {
|
|
86
|
+
state.correlation = -state.fisherZCap;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Compute r² (coefficient of determination) at publish time (if required)
|
|
90
|
+
// by squaring the correlation. r² is always in [0,1] and has a direct
|
|
91
|
+
// percentage interpretation (model quality, sensor agreement, etc).
|
|
92
|
+
if ( state.stats && state.stats.r2 ) {
|
|
93
|
+
state.r2 = state.correlation * state.correlation;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Compute Fisher Z transform only when:
|
|
97
|
+
// - feature enabled (fisherZCap < 1), and
|
|
98
|
+
// - output mapping provided in stats.fisherZ.
|
|
99
|
+
if ( ( state.fisherZCap < 1 ) && state.stats && state.stats.fisherZT ) {
|
|
100
|
+
// Guard against |r| → 1 to prevent infinities; clamp with current cap.
|
|
101
|
+
const r = state.correlation;
|
|
102
|
+
const capped = ( r > state.fisherZCap ) ? state.fisherZCap : ( r < -state.fisherZCap ? -state.fisherZCap : r );
|
|
103
|
+
state.fisherZT = 0.5 * Math.log( ( 1 + capped ) / ( 1 - capped ) );
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return state;
|
|
108
|
+
}; // update()
|
|
109
|
+
|
|
110
|
+
export default update;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel module for the esMean node.
|
|
3
|
+
*
|
|
4
|
+
* Re-exports the standard node lifecycle methods (init, update, reset,
|
|
5
|
+
* recompute, publishTo), shared control signals (disable, enable, pause,
|
|
6
|
+
* unpause), and all introspection metadata.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export { default as init } from './init.js';
|
|
10
|
+
export { default as update } from './update.js';
|
|
11
|
+
export { default as reset } from './reset.js';
|
|
12
|
+
export { default as recompute } from './recompute.js';
|
|
13
|
+
export { default as publishTo } from './publish-to.js';
|
|
14
|
+
// Direct imports for common control functions
|
|
15
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
16
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
17
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
18
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
19
|
+
// Re-export everything from introspection.js
|
|
20
|
+
export * from './introspect.js';
|