@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,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Numerical stability handler for the es-pairwise-correlation node.
|
|
3
|
+
* Floors negative variances (floating-point guard), recomputes all pairwise
|
|
4
|
+
* correlations from current accumulated state, clamps results, and re-derives
|
|
5
|
+
* Fisher Z transforms when enabled.
|
|
6
|
+
*/
|
|
7
|
+
// nodes/es-pairwise-correlation/recompute.js
|
|
8
|
+
|
|
9
|
+
const recompute = function ( state ) {
|
|
10
|
+
// Recompute correlations from current statistics
|
|
11
|
+
// This ensures numerical consistency and fixes any accumulated errors
|
|
12
|
+
|
|
13
|
+
const {
|
|
14
|
+
n, variances, covariances, correlations,
|
|
15
|
+
minVariance, fisherZCap, pairCount
|
|
16
|
+
} = state;
|
|
17
|
+
|
|
18
|
+
// Step 1: Ensure variances are non-negative (fix numerical errors)
|
|
19
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
20
|
+
if ( variances[ i ] < 0 ) {
|
|
21
|
+
variances[ i ] = 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Step 2: Recompute all correlations using direct nested loops
|
|
26
|
+
let idx = 0;
|
|
27
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
28
|
+
// Pre-compute variance for variable i with protection
|
|
29
|
+
const varX = Math.max( variances[ i ], minVariance );
|
|
30
|
+
const stdX = Math.sqrt( varX );
|
|
31
|
+
|
|
32
|
+
for ( let j = i + 1; j < n; j += 1 ) {
|
|
33
|
+
// Pre-compute variance for variable j with protection
|
|
34
|
+
const varY = Math.max( variances[ j ], minVariance );
|
|
35
|
+
const stdY = Math.sqrt( varY );
|
|
36
|
+
|
|
37
|
+
// Compute correlation from covariance
|
|
38
|
+
const cov = covariances[ idx ];
|
|
39
|
+
correlations[ idx ] = cov / ( stdX * stdY );
|
|
40
|
+
|
|
41
|
+
// Ensure valid range [-1, 1] with Fisher Z cap if applicable
|
|
42
|
+
if ( correlations[ idx ] >= fisherZCap ) {
|
|
43
|
+
correlations[ idx ] = fisherZCap;
|
|
44
|
+
} else if ( correlations[ idx ] <= -fisherZCap ) {
|
|
45
|
+
correlations[ idx ] = -fisherZCap;
|
|
46
|
+
} else if ( Number.isNaN( correlations[ idx ] ) ) {
|
|
47
|
+
// Handle edge case where both variances were zero
|
|
48
|
+
correlations[ idx ] = 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
idx += 1;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Step 3: Recompute Fisher Z transformation if present
|
|
56
|
+
if ( state.fisherZT ) {
|
|
57
|
+
for ( let i = 0; i < pairCount; i += 1 ) {
|
|
58
|
+
const r = correlations[ i ];
|
|
59
|
+
// Fisher Z: 0.5 * ln((1+r)/(1-r))
|
|
60
|
+
// Note: r is already clamped to [-fisherZCap, fisherZCap]
|
|
61
|
+
// so we're guaranteed (1+r)/(1-r) > 0
|
|
62
|
+
state.fisherZT[ i ] = 0.5 * Math.log( ( 1 + r ) / ( 1 - r ) );
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return true;
|
|
67
|
+
}; // recompute()
|
|
68
|
+
|
|
69
|
+
export default recompute;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resets all statistical accumulators (means, variances,
|
|
3
|
+
* covariances, correlations) and output values (fisherZT) to their initial
|
|
4
|
+
* state, allowing the node to restart computation from scratch.
|
|
5
|
+
*/
|
|
6
|
+
// nodes/es-pairwise-correlation/reset.js
|
|
7
|
+
|
|
8
|
+
const reset = function ( state ) {
|
|
9
|
+
// Reset all statistics to zero
|
|
10
|
+
state.means.fill( 0 );
|
|
11
|
+
state.variances.fill( 0 );
|
|
12
|
+
state.covariances.fill( 0 );
|
|
13
|
+
state.correlations.fill( 0 );
|
|
14
|
+
|
|
15
|
+
if ( state.fisherZT ) {
|
|
16
|
+
state.fisherZT.fill( 0 );
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Reset sample counter
|
|
20
|
+
state.sampleCount = 0;
|
|
21
|
+
|
|
22
|
+
// Clear workspace arrays
|
|
23
|
+
state.values.fill( 0 );
|
|
24
|
+
state.deltas.fill( 0 );
|
|
25
|
+
|
|
26
|
+
return true;
|
|
27
|
+
}; // reset()
|
|
28
|
+
|
|
29
|
+
export default reset;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Hot-path exponentially weighted pairwise correlation computation.
|
|
3
|
+
* Uses a Welford-style incremental algorithm with exponential decay on means,
|
|
4
|
+
* variance, and covariance for all n*(n-1)/2 variable pairs. Zero allocations;
|
|
5
|
+
* all reads/writes via the pre-allocated state object.
|
|
6
|
+
*/
|
|
7
|
+
// nodes/es-pairwise-correlation/update.js
|
|
8
|
+
|
|
9
|
+
const update = function ( state, msg ) {
|
|
10
|
+
if ( state.disable || state.pause ) return state;
|
|
11
|
+
const {
|
|
12
|
+
x, n, alpha, minVariance,
|
|
13
|
+
values, deltas, means, variances, covariances,
|
|
14
|
+
correlations, fisherZCap, pairCount
|
|
15
|
+
} = state;
|
|
16
|
+
|
|
17
|
+
state.inputValidationFailed = false;
|
|
18
|
+
// Extract and validate & Handle faults gracefully: ensure their isolation
|
|
19
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
20
|
+
const val = msg[ x[ i ] ];
|
|
21
|
+
if ( val === undefined || val === null ) {
|
|
22
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
23
|
+
state.inputValidationFailed = true;
|
|
24
|
+
return state;
|
|
25
|
+
}
|
|
26
|
+
values[ i ] = val;
|
|
27
|
+
if ( !Number.isFinite( values[ i ] ) ) {
|
|
28
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
29
|
+
state.inputValidationFailed = true;
|
|
30
|
+
return state;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Initialize on first sample: set means and count, no variance/covariance yet.
|
|
35
|
+
if ( state.sampleCount === 0 ) {
|
|
36
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
37
|
+
means[ i ] = values[ i ];
|
|
38
|
+
}
|
|
39
|
+
state.sampleCount = 1;
|
|
40
|
+
return state;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Increment sample count for subsequent observations.
|
|
44
|
+
state.sampleCount += 1;
|
|
45
|
+
|
|
46
|
+
// Update deltas, means, variances
|
|
47
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
48
|
+
deltas[ i ] = values[ i ] - means[ i ];
|
|
49
|
+
means[ i ] += alpha * deltas[ i ];
|
|
50
|
+
variances[ i ] += alpha * ( ( deltas[ i ] * ( values[ i ] - means[ i ] ) ) - variances[ i ] );
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Update covariances and correlations
|
|
54
|
+
let idx = 0;
|
|
55
|
+
for ( let i = 0; i < n; i += 1 ) {
|
|
56
|
+
for ( let j = i + 1; j < n; j += 1 ) {
|
|
57
|
+
// Update covariance
|
|
58
|
+
covariances[ idx ] += alpha * ( ( deltas[ i ] * ( values[ j ] - means[ j ] ) ) - covariances[ idx ] );
|
|
59
|
+
|
|
60
|
+
// Compute correlation if ready
|
|
61
|
+
if ( state.sampleCount >= state.minSamples ) {
|
|
62
|
+
const varX = Math.max( variances[ i ], minVariance );
|
|
63
|
+
const varY = Math.max( variances[ j ], minVariance );
|
|
64
|
+
correlations[ idx ] = covariances[ idx ] / Math.sqrt( varX * varY );
|
|
65
|
+
|
|
66
|
+
// Clamp
|
|
67
|
+
if ( correlations[ idx ] >= fisherZCap ) {
|
|
68
|
+
correlations[ idx ] = fisherZCap;
|
|
69
|
+
} else if ( correlations[ idx ] <= -fisherZCap ) {
|
|
70
|
+
correlations[ idx ] = -fisherZCap;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
idx += 1;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fisher Z transform if needed
|
|
79
|
+
if ( state.fisherZT && state.sampleCount >= state.minSamples ) {
|
|
80
|
+
for ( let i = 0; i < pairCount; i += 1 ) {
|
|
81
|
+
const r = correlations[ i ];
|
|
82
|
+
state.fisherZT[ i ] = 0.5 * Math.log( ( 1 + r ) / ( 1 - r ) );
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return state;
|
|
87
|
+
}; // update()
|
|
88
|
+
|
|
89
|
+
export default update;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Barrel module for the esStats 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
|
+
// nodes/es-stats/index.js
|
|
9
|
+
|
|
10
|
+
export { default as init } from './init.js';
|
|
11
|
+
export { default as update } from './update.js';
|
|
12
|
+
export { default as reset } from './reset.js';
|
|
13
|
+
export { default as recompute } from './recompute.js';
|
|
14
|
+
export { default as publishTo } from './publish-to.js';
|
|
15
|
+
// Direct imports for common control functions
|
|
16
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
17
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
18
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
19
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
20
|
+
// Re-export everything from introspection.js
|
|
21
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Initializes esStats node state from a validated spec.
|
|
3
|
+
*
|
|
4
|
+
* Derives the exponential smoothing factor (alpha) from halfLife, pre-allocates
|
|
5
|
+
* all statistical accumulators on a prototype-free state object, and determines
|
|
6
|
+
* which computation paths (Welford for mean/variance, envelope for floor/ceiling)
|
|
7
|
+
* are required by the requested stats. All allocation happens here; the hot path
|
|
8
|
+
* is zero-allocation.
|
|
9
|
+
*/
|
|
10
|
+
// nodes/es-stats/init.js
|
|
11
|
+
|
|
12
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
13
|
+
import * as introspect from './introspect.js';
|
|
14
|
+
import { halfLifeToAlpha } from '../../core/utils/half-life/index.js';
|
|
15
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Initialize ES Stats node with comprehensive streaming statistics.
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} spec - Node specification
|
|
21
|
+
* @returns {Object} Initial state for statistics computation
|
|
22
|
+
*/
|
|
23
|
+
const init = function ( spec ) {
|
|
24
|
+
// Validate against schema
|
|
25
|
+
validateSpec( spec, introspect );
|
|
26
|
+
|
|
27
|
+
// Create state after validation passes
|
|
28
|
+
const state = Object.create( null );
|
|
29
|
+
// Standard input parameter health flag during update: false means healthy
|
|
30
|
+
state.inputValidationFailed = false;
|
|
31
|
+
// Ensure node is enabled by default
|
|
32
|
+
state.disable = false;
|
|
33
|
+
state.pause = false;
|
|
34
|
+
|
|
35
|
+
// Input field
|
|
36
|
+
state.x = spec.from.x;
|
|
37
|
+
|
|
38
|
+
// Config copy for output field mapping
|
|
39
|
+
const stats = spec.stats;
|
|
40
|
+
state.stats = stats;
|
|
41
|
+
|
|
42
|
+
// Half-life configuration with defaults
|
|
43
|
+
// Supports both direct: { halfLife: 20 } and field-keyed: { halfLife: { temp: 5, pressure: 20 } }
|
|
44
|
+
const defaults = introspect.DEFAULT_OPTIONS;
|
|
45
|
+
const halfLifeSpec = resolveScalar( spec.halfLife, state.x );
|
|
46
|
+
state.halfLife = halfLifeSpec ?? defaults.halfLife;
|
|
47
|
+
|
|
48
|
+
// Convert half-life to alpha for hot-path efficiency
|
|
49
|
+
state.alpha = halfLifeToAlpha( state.halfLife );
|
|
50
|
+
state.decay = 1 - state.alpha; // Pre-compute for efficiency
|
|
51
|
+
|
|
52
|
+
// Biased/unbiased estimator choice
|
|
53
|
+
state.biased = spec.biased ?? defaults.biased;
|
|
54
|
+
|
|
55
|
+
// Constants
|
|
56
|
+
state.EPS = 1e-12;
|
|
57
|
+
state.snrDbCap = 60;
|
|
58
|
+
state.cvLarge = 1e6;
|
|
59
|
+
|
|
60
|
+
// Core statistics (Welford's algorithm)
|
|
61
|
+
state.mean = 0;
|
|
62
|
+
state.m2 = 0; // Sum of squared deviations
|
|
63
|
+
state.variance = 0;
|
|
64
|
+
state.stdev = 0;
|
|
65
|
+
|
|
66
|
+
// Envelope statistics (leaky min/max)
|
|
67
|
+
state.floor = 0;
|
|
68
|
+
state.ceiling = 0;
|
|
69
|
+
state.envelope = 0;
|
|
70
|
+
state.mid = 0;
|
|
71
|
+
|
|
72
|
+
// Signal quality metrics
|
|
73
|
+
state.snrDB = 0;
|
|
74
|
+
state.cv = 0;
|
|
75
|
+
|
|
76
|
+
// Current value scores
|
|
77
|
+
state.zScore = 0;
|
|
78
|
+
state.envScore = 0;
|
|
79
|
+
|
|
80
|
+
// Warmup normalization for unbiased mode
|
|
81
|
+
state.weightSum = 0; // Sum of exponentially smoothed weights: 1 - (1 - alpha)^t
|
|
82
|
+
|
|
83
|
+
// Sample counter
|
|
84
|
+
state.sampleCount = 0;
|
|
85
|
+
|
|
86
|
+
// Determine computation paths needed
|
|
87
|
+
state.needsWelford = stats.mean !== undefined ||
|
|
88
|
+
stats.variance !== undefined ||
|
|
89
|
+
stats.stdev !== undefined ||
|
|
90
|
+
stats.snrDB !== undefined ||
|
|
91
|
+
stats.cv !== undefined ||
|
|
92
|
+
stats.zScore !== undefined;
|
|
93
|
+
|
|
94
|
+
state.needsEnvelope = stats.floor !== undefined ||
|
|
95
|
+
stats.ceiling !== undefined ||
|
|
96
|
+
stats.envelope !== undefined ||
|
|
97
|
+
stats.mid !== undefined ||
|
|
98
|
+
stats.envScore !== undefined;
|
|
99
|
+
|
|
100
|
+
state.nodeType = introspect.getNodeType();
|
|
101
|
+
|
|
102
|
+
return state;
|
|
103
|
+
}; // init()
|
|
104
|
+
|
|
105
|
+
export default init;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Single source of truth for esStats node metadata.
|
|
3
|
+
*
|
|
4
|
+
* Exports supported stats (11 metrics across Welford, envelope, and
|
|
5
|
+
* signal-quality categories), human-readable stat descriptions, control
|
|
6
|
+
* methods, node capabilities, default options, and the DSL spec schema
|
|
7
|
+
* with validators. All getter functions return defensive copies.
|
|
8
|
+
*/
|
|
9
|
+
// nodes/es-stats/introspect.js
|
|
10
|
+
|
|
11
|
+
import { validators, composerValidators } from '../../core/utils/validate/index.js';
|
|
12
|
+
|
|
13
|
+
/** Single source of truth for what this plugin can do */
|
|
14
|
+
const SUPPORTED_STATS = [
|
|
15
|
+
// Core Statistics (Welford's algorithm)
|
|
16
|
+
'mean',
|
|
17
|
+
'variance',
|
|
18
|
+
'stdev',
|
|
19
|
+
|
|
20
|
+
// Envelope Statistics (Leaky min/max)
|
|
21
|
+
'floor',
|
|
22
|
+
'ceiling',
|
|
23
|
+
'envelope',
|
|
24
|
+
'mid',
|
|
25
|
+
|
|
26
|
+
// Signal Quality Metrics
|
|
27
|
+
'snrDB',
|
|
28
|
+
'cv',
|
|
29
|
+
|
|
30
|
+
// Current Value Scores (computed per sample)
|
|
31
|
+
'zScore',
|
|
32
|
+
'envScore'
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/** Human-readable descriptions (for introspection) */
|
|
36
|
+
const STAT_DESCRIPTIONS = {
|
|
37
|
+
mean: 'Exponentially smoothed mean',
|
|
38
|
+
variance: 'Exponentially smoothed variance (Welford\'s algorithm)',
|
|
39
|
+
stdev: 'Square root of variance',
|
|
40
|
+
floor: 'Recent minimum with exponential decay (envelope follower)',
|
|
41
|
+
ceiling: 'Recent maximum with exponential decay (envelope follower)',
|
|
42
|
+
envelope: 'Signal envelope width (ceiling - floor)',
|
|
43
|
+
mid: 'Envelope midpoint ((floor + ceiling) / 2)',
|
|
44
|
+
snrDB: 'Signal-to-noise ratio in dB (20*log10(|mean|/stdev))',
|
|
45
|
+
cv: 'Coefficient of variation (stdev/|mean|)',
|
|
46
|
+
zScore: 'Normalized deviation of current value ((x - mean)/stdev)',
|
|
47
|
+
envScore: 'Envelope-normalized score ((x - mid)/(envelope/2))'
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Control methods available for triggers */
|
|
51
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
52
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
53
|
+
enable: 'Enables node processing',
|
|
54
|
+
disable: 'Disables node processing',
|
|
55
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
56
|
+
unpause: 'Resumes node processing after pause'
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** The type of this node */
|
|
60
|
+
const NODE_TYPE = 'ES Stats';
|
|
61
|
+
|
|
62
|
+
/** Node capabilities for documentation */
|
|
63
|
+
const CAPABILITIES = {
|
|
64
|
+
description: 'Comprehensive exponentially weighted statistics for streaming signals',
|
|
65
|
+
features: [
|
|
66
|
+
'Numerically stable Welford\'s algorithm for mean/variance',
|
|
67
|
+
'Leaky envelope tracking (floor/ceiling) with fast attack/slow release',
|
|
68
|
+
'Signal quality metrics (SNR, CV)',
|
|
69
|
+
'Real-time anomaly scores (z-score, envelope score)',
|
|
70
|
+
'No allocations in hot path',
|
|
71
|
+
'Single half-life for consistent weighting'
|
|
72
|
+
]
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// Export functions for accessing metadata
|
|
76
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
77
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
78
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
79
|
+
export const getNodeType = () => NODE_TYPE;
|
|
80
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
81
|
+
|
|
82
|
+
export const DEFAULT_OPTIONS = {
|
|
83
|
+
halfLife: 10,
|
|
84
|
+
biased: false
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// DSL Metadata for transpilation
|
|
88
|
+
const DSL_METADATA = {
|
|
89
|
+
// Full spec schema for validation
|
|
90
|
+
specSchema: {
|
|
91
|
+
nodeType: {
|
|
92
|
+
type: 'string',
|
|
93
|
+
required: true,
|
|
94
|
+
value: NODE_TYPE
|
|
95
|
+
},
|
|
96
|
+
name: {
|
|
97
|
+
type: 'string',
|
|
98
|
+
required: true,
|
|
99
|
+
validator: validators.identifier,
|
|
100
|
+
error: 'Name must be a valid identifier'
|
|
101
|
+
},
|
|
102
|
+
from: {
|
|
103
|
+
type: 'object',
|
|
104
|
+
required: true,
|
|
105
|
+
properties: {
|
|
106
|
+
x: {
|
|
107
|
+
type: 'string',
|
|
108
|
+
required: true,
|
|
109
|
+
validator: validators.noSpaces,
|
|
110
|
+
error: 'Field name cannot contain spaces'
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
halfLife: {
|
|
115
|
+
type: 'numberOrFieldKeyed',
|
|
116
|
+
required: false,
|
|
117
|
+
default: DEFAULT_OPTIONS.halfLife,
|
|
118
|
+
validator: composerValidators.halfLife,
|
|
119
|
+
error: 'halfLife must be > 0 and < 999999'
|
|
120
|
+
},
|
|
121
|
+
biased: {
|
|
122
|
+
type: 'boolean',
|
|
123
|
+
required: false,
|
|
124
|
+
default: DEFAULT_OPTIONS.biased
|
|
125
|
+
},
|
|
126
|
+
stats: {
|
|
127
|
+
type: 'object',
|
|
128
|
+
required: true,
|
|
129
|
+
minProperties: 1,
|
|
130
|
+
propertyNames: SUPPORTED_STATS,
|
|
131
|
+
propertySchema: {
|
|
132
|
+
type: 'object',
|
|
133
|
+
properties: {
|
|
134
|
+
storeAs: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
required: true,
|
|
137
|
+
validator: validators.identifier,
|
|
138
|
+
error: 'storeAs must be a valid identifier'
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
// Build spec from DSL parameters
|
|
146
|
+
// Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
|
|
147
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
148
|
+
nodeType: NODE_TYPE,
|
|
149
|
+
name,
|
|
150
|
+
from: { x },
|
|
151
|
+
stats,
|
|
152
|
+
...options
|
|
153
|
+
} ),
|
|
154
|
+
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Copies requested esStats statistics onto the message for
|
|
3
|
+
* downstream consumption.
|
|
4
|
+
*
|
|
5
|
+
* Propagates NaN for all configured stats when input validation has failed.
|
|
6
|
+
* Gates output until the warmup period (3 samples) is complete to ensure
|
|
7
|
+
* statistical meaningfulness. Iterates only over stats declared in the spec;
|
|
8
|
+
* zero allocations on the hot path.
|
|
9
|
+
*/
|
|
10
|
+
// nodes/es-stats/publish-to.js
|
|
11
|
+
|
|
12
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Publish computed statistics to message.
|
|
16
|
+
* Only publishes stats that were requested and after warmup.
|
|
17
|
+
*
|
|
18
|
+
* @param {Object} state - Node state with computed statistics
|
|
19
|
+
* @param {Object} msg - Message to annotate with outputs
|
|
20
|
+
*/
|
|
21
|
+
const publishTo = function ( state, msg ) {
|
|
22
|
+
if ( state.disable ) return;
|
|
23
|
+
// Propagate NaN if unhealthy
|
|
24
|
+
if ( state.inputValidationFailed ) {
|
|
25
|
+
publishNaN( state, msg );
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Only publish after at least 3 samples for statistical meaning
|
|
29
|
+
if ( state.sampleCount < 3 ) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Iterate only over configured stats
|
|
34
|
+
const stats = state.stats;
|
|
35
|
+
for ( const statName in stats ) {
|
|
36
|
+
if ( Object.prototype.hasOwnProperty.call( stats, statName ) ) {
|
|
37
|
+
msg[ stats[ statName ].storeAs ] = state[ statName ];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}; // publishTo()
|
|
41
|
+
|
|
42
|
+
export default publishTo;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Numerical stability handler for the esStats node.
|
|
3
|
+
*
|
|
4
|
+
* Floors negative m2 and variance to zero, corrects inverted floor/ceiling
|
|
5
|
+
* order, and recomputes variance from m2 using the appropriate normalization
|
|
6
|
+
* (biased or unbiased). On catastrophic NaN/Infinity in critical state fields,
|
|
7
|
+
* triggers a full reset. Since exponentially weighted statistics have inherently
|
|
8
|
+
* bounded state (each update is a weighted blend, not unbounded accumulation),
|
|
9
|
+
* consistency corrections suffice — no sample buffer is available for full
|
|
10
|
+
* recomputation.
|
|
11
|
+
*/
|
|
12
|
+
// nodes/es-stats/recompute.js
|
|
13
|
+
|
|
14
|
+
import reset from './reset.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Recompute statistics from current state to ensure numerical consistency.
|
|
18
|
+
* Handles numerical drift in long-running streams.
|
|
19
|
+
*
|
|
20
|
+
* @param {Object} state - Node state
|
|
21
|
+
* @returns {boolean} Always true (successful recomputation)
|
|
22
|
+
*/
|
|
23
|
+
const recompute = function ( state ) {
|
|
24
|
+
// Ensure m2 is non-negative (second moment)
|
|
25
|
+
if ( state.m2 < 0 ) {
|
|
26
|
+
state.m2 = 0;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Ensure variance is non-negative
|
|
30
|
+
if ( state.variance < 0 ) {
|
|
31
|
+
state.variance = 0;
|
|
32
|
+
state.stdev = 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const stats = state.stats;
|
|
36
|
+
// Recompute variance from m2 to ensure consistency
|
|
37
|
+
if ( state.sampleCount >= 2 && stats.variance !== undefined ) {
|
|
38
|
+
if ( state.biased ) {
|
|
39
|
+
state.variance = state.m2;
|
|
40
|
+
} else {
|
|
41
|
+
const w = state.weightSum;
|
|
42
|
+
state.variance = ( w > 1e-12 ) ? ( state.m2 / w ) : state.m2;
|
|
43
|
+
}
|
|
44
|
+
state.stdev = Math.sqrt( state.variance );
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Ensure envelope consistency
|
|
48
|
+
if ( stats.envelope !== undefined ) {
|
|
49
|
+
// Floor should not exceed ceiling
|
|
50
|
+
if ( state.floor > state.ceiling ) {
|
|
51
|
+
const temp = state.floor;
|
|
52
|
+
state.floor = state.ceiling;
|
|
53
|
+
state.ceiling = temp;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Recompute derived envelope metrics
|
|
57
|
+
state.envelope = state.ceiling - state.floor;
|
|
58
|
+
state.mid = ( state.floor + state.ceiling ) * 0.5;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Check for NaN and reset if found
|
|
62
|
+
if ( !Number.isFinite( state.mean ) ||
|
|
63
|
+
!Number.isFinite( state.variance ) ||
|
|
64
|
+
!Number.isFinite( state.floor ) ||
|
|
65
|
+
!Number.isFinite( state.ceiling ) ) {
|
|
66
|
+
// Catastrophic numerical failure - reset
|
|
67
|
+
reset( state );
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return true;
|
|
72
|
+
}; // recompute()
|
|
73
|
+
|
|
74
|
+
export default recompute;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resets all esStats accumulated state to its pre-observation
|
|
3
|
+
* condition.
|
|
4
|
+
*
|
|
5
|
+
* Clears mean, m2, variance, stdev, envelope metrics, signal-quality scores,
|
|
6
|
+
* weightSum, and sampleCount. Configuration (alpha, decay, halfLife, biased,
|
|
7
|
+
* stats, computation-path flags) is preserved so the next observation seeds
|
|
8
|
+
* the estimate afresh. Returns true per ADR-004 control-signal contract.
|
|
9
|
+
*/
|
|
10
|
+
// nodes/es-stats/reset.js
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Reset all statistical accumulators to initial state.
|
|
14
|
+
* Called by control signals to restart statistics tracking.
|
|
15
|
+
*
|
|
16
|
+
* @param {Object} state - Node state to reset
|
|
17
|
+
* @returns {boolean} Always true (ADR-004)
|
|
18
|
+
*/
|
|
19
|
+
const reset = function ( state ) {
|
|
20
|
+
// Reset core statistics
|
|
21
|
+
state.mean = 0;
|
|
22
|
+
state.m2 = 0;
|
|
23
|
+
state.variance = 0;
|
|
24
|
+
state.stdev = 0;
|
|
25
|
+
|
|
26
|
+
// Reset envelope statistics
|
|
27
|
+
state.floor = 0;
|
|
28
|
+
state.ceiling = 0;
|
|
29
|
+
state.envelope = 0;
|
|
30
|
+
state.mid = 0;
|
|
31
|
+
|
|
32
|
+
// Reset signal quality
|
|
33
|
+
state.snrDB = 0;
|
|
34
|
+
state.cv = 0;
|
|
35
|
+
|
|
36
|
+
// Reset current value scores
|
|
37
|
+
state.zScore = 0;
|
|
38
|
+
state.envScore = 0;
|
|
39
|
+
|
|
40
|
+
// Reset weight accumulator
|
|
41
|
+
state.weightSum = 0;
|
|
42
|
+
|
|
43
|
+
// Reset sample counter
|
|
44
|
+
state.sampleCount = 0;
|
|
45
|
+
|
|
46
|
+
return true;
|
|
47
|
+
}; // reset()
|
|
48
|
+
|
|
49
|
+
export default reset;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Leaky envelope follower with fast-attack / slow-release
|
|
3
|
+
* semantics for the esStats node.
|
|
4
|
+
*
|
|
5
|
+
* New extremes are adopted immediately (fast attack); non-extreme values
|
|
6
|
+
* relax floor/ceiling toward the current value at rate alpha (slow release).
|
|
7
|
+
* Computes envelope width (ceiling - floor), midpoint, and envScore
|
|
8
|
+
* (normalized distance from midpoint, unclamped to preserve severity).
|
|
9
|
+
*
|
|
10
|
+
* Zero allocations; all reads/writes via the pre-allocated state object.
|
|
11
|
+
*/
|
|
12
|
+
// nodes/es-stats/update-envelope.js
|
|
13
|
+
|
|
14
|
+
const updateEnvelope = function ( state, xVal ) {
|
|
15
|
+
// Envelope Score: Normalized distance from range midpoint
|
|
16
|
+
// Score = (value - mid) / halfRange; 0=center, ±1=bounds, >1=outside
|
|
17
|
+
// Computed BEFORE update to detect range breakouts
|
|
18
|
+
// Unclamped to preserve severity (e.g., score=3 means 3x beyond normal)
|
|
19
|
+
if ( state.stats.envScore ) {
|
|
20
|
+
const halfEnv = state.envelope * 0.5;
|
|
21
|
+
state.envScore = ( halfEnv > state.EPS ) ?
|
|
22
|
+
( ( xVal - state.mid ) / halfEnv ) : 0;
|
|
23
|
+
// optional clamp:
|
|
24
|
+
// state.envScore = Math.max( -1, Math.min( 1, state.envScore ) );
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// NOW update envelope with fast-attack/slow-release
|
|
28
|
+
if ( xVal < state.floor ) {
|
|
29
|
+
state.floor = xVal;
|
|
30
|
+
} else {
|
|
31
|
+
state.floor += ( state.alpha * ( xVal - state.floor ) );
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if ( xVal > state.ceiling ) {
|
|
35
|
+
state.ceiling = xVal;
|
|
36
|
+
} else {
|
|
37
|
+
state.ceiling -= ( state.alpha * ( state.ceiling - xVal ) );
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
state.envelope = state.ceiling - state.floor;
|
|
41
|
+
state.mid = ( state.floor + state.ceiling ) * 0.5;
|
|
42
|
+
}; // updateEnvelope()
|
|
43
|
+
|
|
44
|
+
export default updateEnvelope;
|