@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,15 @@
|
|
|
1
|
+
// nodes/trend/recompute.js
|
|
2
|
+
|
|
3
|
+
const recompute = function ( state ) {
|
|
4
|
+
// Ensure rate variance stays bounded
|
|
5
|
+
if ( state.rocVariance < 0 ) {
|
|
6
|
+
state.rocVariance = 0;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// No long-running accumulations that need recomputation
|
|
10
|
+
// Rate-based algorithm is inherently stable
|
|
11
|
+
|
|
12
|
+
return true;
|
|
13
|
+
}; // recompute()
|
|
14
|
+
|
|
15
|
+
export default recompute;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// nodes/trend/reset.js
|
|
2
|
+
|
|
3
|
+
const reset = function ( state ) {
|
|
4
|
+
state.previousValue = null;
|
|
5
|
+
|
|
6
|
+
state.rocMean = 0;
|
|
7
|
+
state.rocVariance = 0;
|
|
8
|
+
|
|
9
|
+
if ( state.stats.accelerationHint ) {
|
|
10
|
+
state.rocSmoothedFast = 0;
|
|
11
|
+
state.accelerationHint = null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
state.samples = 0;
|
|
15
|
+
state.consistentSamples = 0;
|
|
16
|
+
|
|
17
|
+
state.previousTrend = 'learning';
|
|
18
|
+
state.trend = 'learning';
|
|
19
|
+
state.confidence = 0;
|
|
20
|
+
// Clear error suppression so next error episode is logged
|
|
21
|
+
state.tunableErrorLogged = false;
|
|
22
|
+
|
|
23
|
+
return true;
|
|
24
|
+
}; // reset()
|
|
25
|
+
|
|
26
|
+
export default reset;
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// nodes/trend/update.js
|
|
2
|
+
|
|
3
|
+
import { computeConfidence } from './compute-confidence.js';
|
|
4
|
+
|
|
5
|
+
const update = function ( state, msg ) {
|
|
6
|
+
if ( state.disable || state.pause ) return state;
|
|
7
|
+
const xVal = msg[ state.x ];
|
|
8
|
+
// Reset on each update
|
|
9
|
+
state.inputValidationFailed = false;
|
|
10
|
+
// Handle faults gracefully: ensure their isolation
|
|
11
|
+
if ( !Number.isFinite( xVal ) ) {
|
|
12
|
+
// Signals publishing NaN for all demanded `stats` in publishTo.
|
|
13
|
+
state.inputValidationFailed = true;
|
|
14
|
+
return state;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
state.samples += 1;
|
|
18
|
+
|
|
19
|
+
if ( state.previousValue === null ) {
|
|
20
|
+
state.previousValue = xVal;
|
|
21
|
+
// This ensures the first roc value is a real one and not 0.
|
|
22
|
+
return state;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Step 1: Lag(1) for roc
|
|
26
|
+
const roc = xVal - state.previousValue;
|
|
27
|
+
state.previousValue = xVal;
|
|
28
|
+
|
|
29
|
+
// Step 2: Track roc statistics
|
|
30
|
+
const rocResidual = roc - state.rocMean;
|
|
31
|
+
state.rocMean += ( state.rocAlpha * rocResidual );
|
|
32
|
+
state.rocVariance += ( state.rocAlpha * ( ( rocResidual * rocResidual ) - state.rocVariance ) );
|
|
33
|
+
|
|
34
|
+
// Step 3: Track roc at a faster roc for acceleration hint
|
|
35
|
+
if ( state.stats.accelerationHint ) {
|
|
36
|
+
state.rocSmoothedFast += ( state.accelAlpha * ( roc - state.rocSmoothedFast ) );
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Step 4: Classification
|
|
40
|
+
state.previousTrend = state.trend;
|
|
41
|
+
|
|
42
|
+
// Resolve rocThreshold tunable for this message
|
|
43
|
+
try {
|
|
44
|
+
// If tunable throws, last good value is retained.
|
|
45
|
+
state.rocThreshold = state.rocThresholdFn( msg );
|
|
46
|
+
if ( state.tunableErrorLogged ) state.tunableErrorLogged = false;
|
|
47
|
+
} catch ( error ) {
|
|
48
|
+
if ( !state.tunableErrorLogged ) {
|
|
49
|
+
state.tunableErrorLogged = true;
|
|
50
|
+
console.error( `WinkComposer/${state.nodeType}: tunable threw: ${error.message}` );
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if ( state.samples < state.warmupSamples ) {
|
|
55
|
+
state.trend = 'learning';
|
|
56
|
+
} else if ( Math.abs( state.rocMean ) < state.rocThreshold ) {
|
|
57
|
+
state.trend = 'stable';
|
|
58
|
+
} else {
|
|
59
|
+
state.trend = state.rocMean > 0 ? 'rising' : 'falling';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Track consistency
|
|
63
|
+
if ( state.trend === state.previousTrend ) {
|
|
64
|
+
state.consistentSamples += 1;
|
|
65
|
+
} else {
|
|
66
|
+
state.consistentSamples = 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Step 5: Confidence computation
|
|
70
|
+
state.confidence = computeConfidence( state );
|
|
71
|
+
|
|
72
|
+
// Step 6: Acceleration hint
|
|
73
|
+
if ( state.stats.accelerationHint && ( state.samples > ( state.warmupSamplesForAccel ) ) ) {
|
|
74
|
+
// fast > mean ⇒ likely_accelerating
|
|
75
|
+
const accelProxy = state.rocSmoothedFast - state.rocMean;
|
|
76
|
+
|
|
77
|
+
// Basic SNR & persistence gates
|
|
78
|
+
const passesSNR = state.snr >= 1.5; // require decent roc SNR
|
|
79
|
+
const accelThreshold = 0.75 * state.rocThreshold; // derive from current rocThreshold
|
|
80
|
+
const passesBand = Math.abs( accelProxy ) >= accelThreshold; // ignore tiny curvature
|
|
81
|
+
const persistent = state.consistentSamples >= 3; // brief dwell
|
|
82
|
+
|
|
83
|
+
if ( passesSNR && passesBand && persistent ) {
|
|
84
|
+
state.accelerationHint = ( accelProxy > 0 ) ? 'likely_accelerating' : 'likely_decelerating';
|
|
85
|
+
} else {
|
|
86
|
+
state.accelerationHint = null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return state;
|
|
91
|
+
}; // update()
|
|
92
|
+
|
|
93
|
+
export default update;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// nodes/tw-stats/copy-from.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Copies Pébay accumulators from source to destination.
|
|
5
|
+
* Used to snapshot live state before reset at window completion or flush.
|
|
6
|
+
*/
|
|
7
|
+
export const copyFrom = function ( source, destination ) {
|
|
8
|
+
destination.n = source.n;
|
|
9
|
+
destination.M1 = source.M1;
|
|
10
|
+
destination.M2 = source.M2;
|
|
11
|
+
destination.M3 = source.M3;
|
|
12
|
+
destination.M4 = source.M4;
|
|
13
|
+
destination.min = source.min;
|
|
14
|
+
destination.max = source.max;
|
|
15
|
+
return destination;
|
|
16
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// nodes/tw-stats/flush.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Forces publication of incomplete window.
|
|
5
|
+
* Used for graceful shutdown or event-driven snapshots.
|
|
6
|
+
*/
|
|
7
|
+
const flush = function ( state ) {
|
|
8
|
+
state.flushLatched = true;
|
|
9
|
+
return true;
|
|
10
|
+
}; // flush()
|
|
11
|
+
|
|
12
|
+
export default flush;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// nodes/tw-stats/index.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview
|
|
5
|
+
* TW Stats — Tumbling window statistics with Pébay's numerically stable
|
|
6
|
+
* algorithm for incremental moment accumulation.
|
|
7
|
+
*
|
|
8
|
+
* ────────────────────────────────────────────────────────────────────────
|
|
9
|
+
* Replaces the two-node chain (momentsDigest → digestMoments) for the
|
|
10
|
+
* common case of computing statistics over count-based tumbling windows.
|
|
11
|
+
*
|
|
12
|
+
* Key properties:
|
|
13
|
+
* • O(1) memory per partition — no ring buffer; Pébay incremental accumulation
|
|
14
|
+
* • Selective moment accumulation — maxMoment (1–4) resolved at init from
|
|
15
|
+
* demanded stats; if/then gating in update, not closures (100K-safe)
|
|
16
|
+
* • Deferred stat conversion — moment-to-stat formulas applied only in
|
|
17
|
+
* publishTo on window completion or flush, not every update tick
|
|
18
|
+
* • Invalid samples skipped (not counted); window fills with valid samples only
|
|
19
|
+
* • Publishes on window completion or flush; scrubs to undefined on other ticks
|
|
20
|
+
* • Supports windowSize from 4 to 1,000,000
|
|
21
|
+
*
|
|
22
|
+
* ────────────────────────────────────────────────────────────────────────
|
|
23
|
+
* References
|
|
24
|
+
* [1] Pébay, P. (2008). Formulas for robust, one-pass parallel computation
|
|
25
|
+
* of covariances and arbitrary-order statistical moments.
|
|
26
|
+
* Sandia Report SAND2008-6212.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
export { default as init } from './init.js';
|
|
30
|
+
export { default as update } from './update.js';
|
|
31
|
+
export { default as reset } from './reset.js';
|
|
32
|
+
export { default as recompute } from './recompute.js';
|
|
33
|
+
export { default as publishTo } from './publish-to.js';
|
|
34
|
+
export { default as flush } from './flush.js';
|
|
35
|
+
// Direct imports for common control functions
|
|
36
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
37
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
38
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
39
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
40
|
+
// Re-export everything from introspection.js
|
|
41
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// nodes/tw-stats/init.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Initialization for twStats node.
|
|
5
|
+
*
|
|
6
|
+
* Resolves the selective accumulation tier (maxMoment 1–4) from demanded
|
|
7
|
+
* stats at init time. All allocations happen here; update() is zero-alloc.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
11
|
+
import * as introspect from './introspect.js';
|
|
12
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
13
|
+
|
|
14
|
+
// Stats that require M2 accumulation
|
|
15
|
+
const NEEDS_M2 = [ 'variance', 'stddev', 'cv', 'skew', 'kurtosis', 'rms', 'crestFactor' ];
|
|
16
|
+
|
|
17
|
+
const init = function ( spec ) {
|
|
18
|
+
validateSpec( spec, introspect );
|
|
19
|
+
|
|
20
|
+
const state = Object.create( null );
|
|
21
|
+
|
|
22
|
+
// Standard flags (all nodes)
|
|
23
|
+
state.inputValidationFailed = false;
|
|
24
|
+
state.disable = false;
|
|
25
|
+
state.pause = false;
|
|
26
|
+
|
|
27
|
+
// Preserve node's name — used for msg[name] = true on publish
|
|
28
|
+
state.name = spec.name;
|
|
29
|
+
|
|
30
|
+
// Input field name
|
|
31
|
+
state.x = spec.from.x;
|
|
32
|
+
|
|
33
|
+
// Resolve options (field-keyed support for windowSize)
|
|
34
|
+
const windowSizeSpec = resolveScalar( spec.windowSize, state.x );
|
|
35
|
+
state.windowSize = windowSizeSpec ?? introspect.DEFAULT_OPTIONS.windowSize;
|
|
36
|
+
state.biased = spec.biased || introspect.DEFAULT_OPTIONS.biased;
|
|
37
|
+
state.epsilon = spec.epsilon ?? introspect.DEFAULT_OPTIONS.epsilon;
|
|
38
|
+
|
|
39
|
+
// Copy stats config (Object.create(null) for clean prototype)
|
|
40
|
+
state.stats = Object.assign( Object.create( null ), spec.stats );
|
|
41
|
+
|
|
42
|
+
// Determine selective accumulation tier from demanded stats
|
|
43
|
+
const statsList = Object.keys( state.stats );
|
|
44
|
+
const needsM2 = statsList.some( ( s ) => NEEDS_M2.includes( s ) );
|
|
45
|
+
const needsM3 = statsList.includes( 'skew' ) || statsList.includes( 'kurtosis' );
|
|
46
|
+
const needsM4 = statsList.includes( 'kurtosis' );
|
|
47
|
+
|
|
48
|
+
if ( needsM4 ) {
|
|
49
|
+
state.maxMoment = 4;
|
|
50
|
+
} else if ( needsM3 ) {
|
|
51
|
+
state.maxMoment = 3;
|
|
52
|
+
} else if ( needsM2 ) {
|
|
53
|
+
state.maxMoment = 2;
|
|
54
|
+
} else {
|
|
55
|
+
state.maxMoment = 1;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
state.needsMinMax = !!( state.stats.min || state.stats.max || state.stats.crestFactor );
|
|
59
|
+
state.needsRms = !!( state.stats.rms || state.stats.crestFactor );
|
|
60
|
+
|
|
61
|
+
// Pébay algorithm accumulators
|
|
62
|
+
state.n = 0;
|
|
63
|
+
state.M1 = 0;
|
|
64
|
+
state.M2 = 0;
|
|
65
|
+
state.M3 = 0;
|
|
66
|
+
state.M4 = 0;
|
|
67
|
+
state.min = Infinity;
|
|
68
|
+
state.max = -Infinity;
|
|
69
|
+
|
|
70
|
+
// Window management
|
|
71
|
+
state.currentCount = 0;
|
|
72
|
+
|
|
73
|
+
// Publish control
|
|
74
|
+
state.planPublish = false;
|
|
75
|
+
state.flushLatched = false;
|
|
76
|
+
|
|
77
|
+
// Snapshot: holds moments at window completion for publishTo to read
|
|
78
|
+
state.snapshot = Object.create( null );
|
|
79
|
+
|
|
80
|
+
// Pre-compute scrubKeys for efficient non-publish scrubbing
|
|
81
|
+
state.scrubKeys = [];
|
|
82
|
+
// eslint-disable-next-line guard-for-in
|
|
83
|
+
for ( const stat in state.stats ) {
|
|
84
|
+
state.scrubKeys.push( state.stats[ stat ].storeAs );
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
state.nodeType = introspect.getNodeType();
|
|
88
|
+
|
|
89
|
+
return state;
|
|
90
|
+
}; // init()
|
|
91
|
+
|
|
92
|
+
export default init;
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
// nodes/tw-stats/introspect.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview Introspection module for twStats node.
|
|
5
|
+
*
|
|
6
|
+
* Tumbling window statistics: accumulates Pébay moments over a count-based
|
|
7
|
+
* window, publishes computed statistics (mean, variance, stddev, cv, skew,
|
|
8
|
+
* kurtosis, min, max, n) on window completion or flush.
|
|
9
|
+
*
|
|
10
|
+
* Selective accumulation: only the moments needed by demanded stats are
|
|
11
|
+
* computed on the hot path. The maxMoment tier (1–4) is resolved at init.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
15
|
+
|
|
16
|
+
/** Single source of truth for what statistics this node can compute */
|
|
17
|
+
const SUPPORTED_STATS = [
|
|
18
|
+
'n',
|
|
19
|
+
'mean',
|
|
20
|
+
'variance',
|
|
21
|
+
'stddev',
|
|
22
|
+
'cv',
|
|
23
|
+
'skew',
|
|
24
|
+
'kurtosis',
|
|
25
|
+
'min',
|
|
26
|
+
'max',
|
|
27
|
+
'rms',
|
|
28
|
+
'crestFactor'
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/** Human-readable descriptions (for introspection) */
|
|
32
|
+
const STAT_DESCRIPTIONS = {
|
|
33
|
+
n: 'Number of valid samples in the completed window',
|
|
34
|
+
mean: 'Arithmetic mean (first moment M1)',
|
|
35
|
+
variance: 'Sample variance: M2 / (n - 1) for unbiased, M2 / n for biased',
|
|
36
|
+
stddev: 'Sample standard deviation: sqrt(variance)',
|
|
37
|
+
cv: 'Coefficient of variation: stddev / |mean|',
|
|
38
|
+
skew: 'Population skewness: m3 / m2^1.5',
|
|
39
|
+
kurtosis: 'Population excess kurtosis: m4 / m2^2 - 3',
|
|
40
|
+
min: 'Minimum value across all samples in the window',
|
|
41
|
+
max: 'Maximum value across all samples in the window',
|
|
42
|
+
rms: 'Root mean square: sqrt(M2/n + M1²)',
|
|
43
|
+
crestFactor: 'Crest factor: peak / RMS — max(|min|, |max|) / rms'
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
/** Control methods available for triggers */
|
|
47
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
48
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
49
|
+
enable: 'Enables node processing',
|
|
50
|
+
disable: 'Disables node processing',
|
|
51
|
+
pause: 'Pauses update processing while allowing publishTo to continue',
|
|
52
|
+
unpause: 'Resumes update processing after a pause',
|
|
53
|
+
flush: 'Forces immediate output of partial window'
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/** Node capabilities for documentation */
|
|
57
|
+
const CAPABILITIES = {
|
|
58
|
+
description: 'Tumbling window statistics using Pébay\'s numerically stable algorithm',
|
|
59
|
+
features: [
|
|
60
|
+
'Configurable window-based aggregation (4 to 1,000,000 samples)',
|
|
61
|
+
'Selective moment accumulation: only computes moments needed by demanded stats',
|
|
62
|
+
'Deferred stat conversion: formulas applied only at publish time',
|
|
63
|
+
'Numerically stable incremental computation (Pébay 2008)',
|
|
64
|
+
'Invalid samples skipped without corrupting statistics',
|
|
65
|
+
'Flush support for graceful shutdown with partial windows',
|
|
66
|
+
'Zero allocations in hot path for maximum throughput',
|
|
67
|
+
'O(1) memory regardless of window size'
|
|
68
|
+
]
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
/** The type of this node */
|
|
72
|
+
const NODE_TYPE = 'TW Stats';
|
|
73
|
+
|
|
74
|
+
// Attach static metadata once, from our constants by making a shallow copy.
|
|
75
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
76
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
77
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
78
|
+
export const getNodeType = () => NODE_TYPE;
|
|
79
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
80
|
+
|
|
81
|
+
export const DEFAULT_OPTIONS = {
|
|
82
|
+
windowSize: 100,
|
|
83
|
+
biased: false,
|
|
84
|
+
epsilon: 1e-12
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// DSL Metadata for transpilation
|
|
88
|
+
const DSL_METADATA = {
|
|
89
|
+
// 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
|
+
windowSize: {
|
|
115
|
+
type: 'numberOrFieldKeyed',
|
|
116
|
+
required: false,
|
|
117
|
+
default: DEFAULT_OPTIONS.windowSize,
|
|
118
|
+
integer: true,
|
|
119
|
+
min: 4,
|
|
120
|
+
max: 1000000,
|
|
121
|
+
validator: validators.positiveInteger,
|
|
122
|
+
error: 'Window size must be a positive integer; minimum 4, maximum 1,000,000'
|
|
123
|
+
},
|
|
124
|
+
biased: {
|
|
125
|
+
type: 'boolean',
|
|
126
|
+
required: false,
|
|
127
|
+
default: DEFAULT_OPTIONS.biased
|
|
128
|
+
},
|
|
129
|
+
epsilon: {
|
|
130
|
+
type: 'number',
|
|
131
|
+
required: false,
|
|
132
|
+
default: DEFAULT_OPTIONS.epsilon,
|
|
133
|
+
min: 0,
|
|
134
|
+
error: 'Epsilon must be a non-negative number'
|
|
135
|
+
},
|
|
136
|
+
stats: {
|
|
137
|
+
type: 'object',
|
|
138
|
+
required: true,
|
|
139
|
+
minProperties: 1,
|
|
140
|
+
propertyNames: SUPPORTED_STATS,
|
|
141
|
+
propertySchema: {
|
|
142
|
+
type: 'object',
|
|
143
|
+
required: true,
|
|
144
|
+
properties: {
|
|
145
|
+
storeAs: {
|
|
146
|
+
type: 'string',
|
|
147
|
+
required: true,
|
|
148
|
+
minLength: 1,
|
|
149
|
+
validator: validators.identifier,
|
|
150
|
+
error: 'storeAs must be a valid identifier'
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
// Cross-field validators (none needed for twStats)
|
|
158
|
+
crossFieldValidators: [],
|
|
159
|
+
|
|
160
|
+
// How to build the spec from DSL params
|
|
161
|
+
// Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
|
|
162
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
163
|
+
nodeType: NODE_TYPE,
|
|
164
|
+
name,
|
|
165
|
+
from: { x },
|
|
166
|
+
stats,
|
|
167
|
+
...options
|
|
168
|
+
} )
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// nodes/tw-stats/publish-to.js
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview TW Stats Publish — deferred stat conversion from snapshot.
|
|
5
|
+
*
|
|
6
|
+
* On publish ticks (window completion or flush): computes final statistics
|
|
7
|
+
* from the Pébay moment snapshot and writes them to the message.
|
|
8
|
+
*
|
|
9
|
+
* On non-publish ticks: scrubs all storeAs fields to undefined to prevent
|
|
10
|
+
* downstream from reading stale data.
|
|
11
|
+
*
|
|
12
|
+
* No publishNaN — invalid samples are skipped in update(); a flush snapshot
|
|
13
|
+
* planned in the prelude still publishes regardless of the current input.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { computeVariance, computeCV, computeSkew, computeKurtosis } from '../../core/utils/stats/formulas.js';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Publish variance-dependent stats when m2 is above epsilon.
|
|
20
|
+
* Extracted to keep publishTo within nesting depth limits.
|
|
21
|
+
*/
|
|
22
|
+
const publishVarianceStats = function ( state, msg, s, n, m2 ) {
|
|
23
|
+
const variance = computeVariance( s.M2, n, state.biased );
|
|
24
|
+
const stddev = Math.sqrt( variance );
|
|
25
|
+
const stats = state.stats;
|
|
26
|
+
const eps = state.epsilon;
|
|
27
|
+
|
|
28
|
+
if ( stats.variance ) msg[ stats.variance.storeAs ] = variance;
|
|
29
|
+
if ( stats.stddev ) msg[ stats.stddev.storeAs ] = stddev;
|
|
30
|
+
if ( stats.cv ) msg[ stats.cv.storeAs ] = computeCV( stddev, s.M1, eps );
|
|
31
|
+
if ( stats.skew ) msg[ stats.skew.storeAs ] = computeSkew( s.M3, n, m2, eps );
|
|
32
|
+
if ( stats.kurtosis ) msg[ stats.kurtosis.storeAs ] = computeKurtosis( s.M4, n, m2, eps );
|
|
33
|
+
}; // publishVarianceStats()
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Set all variance-dependent stats to NaN.
|
|
37
|
+
* Used when n < 2 or variance is not computable.
|
|
38
|
+
*/
|
|
39
|
+
const publishVarianceNaN = function ( stats, msg ) {
|
|
40
|
+
if ( stats.variance ) msg[ stats.variance.storeAs ] = NaN;
|
|
41
|
+
if ( stats.stddev ) msg[ stats.stddev.storeAs ] = NaN;
|
|
42
|
+
if ( stats.cv ) msg[ stats.cv.storeAs ] = NaN;
|
|
43
|
+
if ( stats.skew ) msg[ stats.skew.storeAs ] = NaN;
|
|
44
|
+
if ( stats.kurtosis ) msg[ stats.kurtosis.storeAs ] = NaN;
|
|
45
|
+
}; // publishVarianceNaN()
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Set variance-dependent stats for near-zero variance (m2 < epsilon).
|
|
49
|
+
* Variance and stddev are 0; cv, skew, kurtosis degenerate.
|
|
50
|
+
*/
|
|
51
|
+
const publishVarianceZero = function ( stats, msg ) {
|
|
52
|
+
if ( stats.variance ) msg[ stats.variance.storeAs ] = 0;
|
|
53
|
+
if ( stats.stddev ) msg[ stats.stddev.storeAs ] = 0;
|
|
54
|
+
if ( stats.cv ) msg[ stats.cv.storeAs ] = NaN;
|
|
55
|
+
if ( stats.skew ) msg[ stats.skew.storeAs ] = 0;
|
|
56
|
+
if ( stats.kurtosis ) msg[ stats.kurtosis.storeAs ] = -3;
|
|
57
|
+
}; // publishVarianceZero()
|
|
58
|
+
|
|
59
|
+
const publishTo = function ( state, msg ) {
|
|
60
|
+
if ( state.disable ) return;
|
|
61
|
+
|
|
62
|
+
if ( state.planPublish ) {
|
|
63
|
+
const s = state.snapshot;
|
|
64
|
+
const n = s.n;
|
|
65
|
+
const stats = state.stats;
|
|
66
|
+
|
|
67
|
+
// Always-available stats
|
|
68
|
+
if ( stats.n ) msg[ stats.n.storeAs ] = n;
|
|
69
|
+
if ( stats.mean ) msg[ stats.mean.storeAs ] = s.M1;
|
|
70
|
+
if ( stats.min ) msg[ stats.min.storeAs ] = s.min;
|
|
71
|
+
if ( stats.max ) msg[ stats.max.storeAs ] = s.max;
|
|
72
|
+
|
|
73
|
+
// Variance-dependent stats (require maxMoment >= 2)
|
|
74
|
+
if ( state.maxMoment >= 2 ) {
|
|
75
|
+
if ( n >= 2 ) {
|
|
76
|
+
const m2 = s.M2 / n;
|
|
77
|
+
if ( m2 >= state.epsilon ) {
|
|
78
|
+
publishVarianceStats( state, msg, s, n, m2 );
|
|
79
|
+
} else {
|
|
80
|
+
publishVarianceZero( stats, msg );
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
publishVarianceNaN( stats, msg );
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Derived stats: RMS and crest factor (cold-path only, from existing accumulators)
|
|
88
|
+
if ( state.needsRms ) {
|
|
89
|
+
const rms = Math.sqrt( ( s.M2 / n ) + ( s.M1 * s.M1 ) );
|
|
90
|
+
if ( stats.rms ) msg[ stats.rms.storeAs ] = rms;
|
|
91
|
+
if ( stats.crestFactor ) {
|
|
92
|
+
const peak = Math.max( Math.abs( s.min ), Math.abs( s.max ) );
|
|
93
|
+
msg[ stats.crestFactor.storeAs ] = ( rms > state.epsilon ) ? ( peak / rms ) : NaN;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
msg[ state.name ] = true;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Non-publish tick: scrub all storeAs fields to undefined
|
|
102
|
+
const keys = state.scrubKeys;
|
|
103
|
+
for ( let i = 0; i < keys.length; i += 1 ) {
|
|
104
|
+
msg[ keys[ i ] ] = undefined;
|
|
105
|
+
}
|
|
106
|
+
}; // publishTo()
|
|
107
|
+
|
|
108
|
+
export default publishTo;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// nodes/tw-stats/recompute.js
|
|
2
|
+
|
|
3
|
+
const recompute = function () {
|
|
4
|
+
// Pébay moments are computed incrementally using a numerically stable
|
|
5
|
+
// algorithm. No recomputation needed (no ring buffer to reconstruct from).
|
|
6
|
+
return true;
|
|
7
|
+
}; // recompute()
|
|
8
|
+
|
|
9
|
+
export default recompute;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// nodes/tw-stats/reset.js
|
|
2
|
+
|
|
3
|
+
const reset = function ( state ) {
|
|
4
|
+
state.n = 0;
|
|
5
|
+
state.M1 = 0;
|
|
6
|
+
state.M2 = 0;
|
|
7
|
+
state.M3 = 0;
|
|
8
|
+
state.M4 = 0;
|
|
9
|
+
state.min = Infinity;
|
|
10
|
+
state.max = -Infinity;
|
|
11
|
+
state.currentCount = 0;
|
|
12
|
+
|
|
13
|
+
return true;
|
|
14
|
+
}; // reset()
|
|
15
|
+
|
|
16
|
+
export default reset;
|