@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,146 @@
|
|
|
1
|
+
import { validators, composerValidators } from '../../core/utils/validate/index.js';
|
|
2
|
+
|
|
3
|
+
/** Single source of truth for what this node can do */
|
|
4
|
+
const SUPPORTED_STATS = [
|
|
5
|
+
'dwellTime',
|
|
6
|
+
'dwellSamples'
|
|
7
|
+
];
|
|
8
|
+
|
|
9
|
+
/** Human-readable descriptions */
|
|
10
|
+
const STAT_DESCRIPTIONS = {
|
|
11
|
+
dwellTime: 'Duration in milliseconds spent in previous state (never negative — a backward clock step reports 0)',
|
|
12
|
+
dwellSamples: 'Number of samples observed in previous state'
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
/** The type of this node */
|
|
16
|
+
const NODE_TYPE = 'State Change Detector';
|
|
17
|
+
|
|
18
|
+
/** Control methods available for triggers */
|
|
19
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
20
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
21
|
+
enable: 'Enables node processing',
|
|
22
|
+
disable: 'Disables node processing',
|
|
23
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
24
|
+
unpause: 'Resumes node processing after pause'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/** Default configuration options */
|
|
28
|
+
const DEFAULT_OPTIONS = {
|
|
29
|
+
debounce: 3,
|
|
30
|
+
changeMode: 'any'
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Node capabilities for documentation */
|
|
34
|
+
const CAPABILITIES = {
|
|
35
|
+
description: 'Detects and confirms transitions between categorical states with debounce filtering',
|
|
36
|
+
features: [
|
|
37
|
+
'Multi-field state monitoring with any/all logic',
|
|
38
|
+
'Debounce protection against boundary oscillation',
|
|
39
|
+
'Dwell time and sample count tracking',
|
|
40
|
+
'Trigger support for adaptive behavior'
|
|
41
|
+
]
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// Export functions for accessing metadata
|
|
45
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
46
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
47
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
48
|
+
export const getNodeType = () => NODE_TYPE;
|
|
49
|
+
export const getCapabilities = () => ( {
|
|
50
|
+
...CAPABILITIES,
|
|
51
|
+
features: CAPABILITIES.features.slice()
|
|
52
|
+
} );
|
|
53
|
+
export { DEFAULT_OPTIONS };
|
|
54
|
+
|
|
55
|
+
// DSL Metadata
|
|
56
|
+
const DSL_METADATA = {
|
|
57
|
+
specSchema: {
|
|
58
|
+
nodeType: {
|
|
59
|
+
type: 'string',
|
|
60
|
+
required: true,
|
|
61
|
+
value: NODE_TYPE
|
|
62
|
+
},
|
|
63
|
+
name: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
required: true,
|
|
66
|
+
validator: validators.identifier,
|
|
67
|
+
error: 'Name must be a valid identifier'
|
|
68
|
+
},
|
|
69
|
+
from: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
required: true,
|
|
72
|
+
properties: {
|
|
73
|
+
x: {
|
|
74
|
+
type: 'array',
|
|
75
|
+
required: true,
|
|
76
|
+
minItems: 1,
|
|
77
|
+
itemSchema: {
|
|
78
|
+
type: 'string',
|
|
79
|
+
validator: validators.noSpaces,
|
|
80
|
+
error: 'Field names must not contain spaces'
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
debounce: {
|
|
86
|
+
type: 'number',
|
|
87
|
+
required: false,
|
|
88
|
+
default: 3,
|
|
89
|
+
validator: composerValidators.windowSize,
|
|
90
|
+
error: 'Debounce must be a positive integer'
|
|
91
|
+
},
|
|
92
|
+
changeMode: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
required: false,
|
|
95
|
+
default: 'any',
|
|
96
|
+
validator: validators.oneOf( [ 'any', 'all' ] ),
|
|
97
|
+
error: 'changeMode must be "any" or "all"'
|
|
98
|
+
},
|
|
99
|
+
timestampField: {
|
|
100
|
+
type: 'string',
|
|
101
|
+
required: false,
|
|
102
|
+
validator: validators.identifier,
|
|
103
|
+
error: 'timestampField must be a valid identifier'
|
|
104
|
+
},
|
|
105
|
+
stats: {
|
|
106
|
+
type: 'object',
|
|
107
|
+
required: true,
|
|
108
|
+
minProperties: 1,
|
|
109
|
+
propertyNames: SUPPORTED_STATS,
|
|
110
|
+
propertySchema: {
|
|
111
|
+
type: 'object',
|
|
112
|
+
properties: {
|
|
113
|
+
storeAs: {
|
|
114
|
+
type: 'string',
|
|
115
|
+
required: true,
|
|
116
|
+
validator: validators.identifier,
|
|
117
|
+
error: 'storeAs must be a valid identifier'
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
},
|
|
122
|
+
triggers: {
|
|
123
|
+
type: 'array',
|
|
124
|
+
required: false,
|
|
125
|
+
itemSchema: {
|
|
126
|
+
type: 'object',
|
|
127
|
+
validator: composerValidators.trigger,
|
|
128
|
+
error: 'Invalid trigger specification'
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
|
|
133
|
+
crossFieldValidators: [],
|
|
134
|
+
|
|
135
|
+
// Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
|
|
136
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
137
|
+
nodeType: NODE_TYPE,
|
|
138
|
+
name,
|
|
139
|
+
from: { x },
|
|
140
|
+
stats,
|
|
141
|
+
...options
|
|
142
|
+
} ),
|
|
143
|
+
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
2
|
+
|
|
3
|
+
const publishTo = function ( state, msg ) {
|
|
4
|
+
if ( state.disable ) return;
|
|
5
|
+
// Propagate NaN if unhealthy
|
|
6
|
+
if ( state.inputValidationFailed ) {
|
|
7
|
+
publishNaN( state, msg );
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Iterate only over configured stats
|
|
12
|
+
const stats = state.stats;
|
|
13
|
+
for ( const statName in stats ) {
|
|
14
|
+
if ( Object.prototype.hasOwnProperty.call(stats, statName ) ) {
|
|
15
|
+
msg[ stats[ statName ].storeAs ] = state[ statName ];
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}; // publishTo()
|
|
19
|
+
|
|
20
|
+
export default publishTo;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const reset = function ( state ) {
|
|
2
|
+
// Reset all fields to null (forces re-initialization)
|
|
3
|
+
for ( let i = 0; i < state.fieldCount; i += 1 ) {
|
|
4
|
+
const field = state.fieldNames[ i ];
|
|
5
|
+
state.prevValues[ field ] = null;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// Reset counters and timing
|
|
9
|
+
state.debounceCount = 0;
|
|
10
|
+
state.stateStartTime = null;
|
|
11
|
+
state.samplesInState = 0;
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
state.dwellSamples = null;
|
|
15
|
+
state.dwellTime = null;
|
|
16
|
+
|
|
17
|
+
return state;
|
|
18
|
+
}; // reset()
|
|
19
|
+
|
|
20
|
+
export default reset;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { executeTriggers } from '../../core/utils/node/index.js';
|
|
2
|
+
import { validateCategoricalFields } from './helpers.js';
|
|
3
|
+
|
|
4
|
+
const update = function ( state, msg ) {
|
|
5
|
+
if ( state.disable || state.pause ) return state;
|
|
6
|
+
// Reset on each update
|
|
7
|
+
state.inputValidationFailed = false;
|
|
8
|
+
// Validate all required fields are present and categorical
|
|
9
|
+
if ( !validateCategoricalFields( msg, state.fieldNames, state.fieldCount ) ) {
|
|
10
|
+
state.inputValidationFailed = true;
|
|
11
|
+
return state;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// These will be set only and only if change is spotted && debounced;
|
|
15
|
+
// otherwise they will remain null.
|
|
16
|
+
state.dwellTime = null;
|
|
17
|
+
state.dwellSamples = null;
|
|
18
|
+
|
|
19
|
+
// Get timestamp once for this message
|
|
20
|
+
const timestamp = state.useTimestampField ? msg[ state.timestampField ] : Date.now();
|
|
21
|
+
|
|
22
|
+
// ADR-004 fault handling: a message-supplied timestamp is a
|
|
23
|
+
// numerical input, so a missing or non-numeric value faults this
|
|
24
|
+
// ONE message and leaves the measurement in progress untouched
|
|
25
|
+
// (same guard as dwellTimeTracker). The device clock never fails
|
|
26
|
+
// this check.
|
|
27
|
+
if ( state.useTimestampField && !Number.isFinite( timestamp ) ) {
|
|
28
|
+
state.inputValidationFailed = true;
|
|
29
|
+
return state;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// First message initialization
|
|
33
|
+
if ( state.prevValues[ state.fieldNames[ 0 ] ] === null ) {
|
|
34
|
+
// Initialize prevValues and timing
|
|
35
|
+
for ( let i = 0; i < state.fieldCount; i += 1 ) {
|
|
36
|
+
const field = state.fieldNames[ i ];
|
|
37
|
+
state.prevValues[ field ] = msg[ field ];
|
|
38
|
+
}
|
|
39
|
+
state.stateStartTime = timestamp;
|
|
40
|
+
state.samplesInState = 1;
|
|
41
|
+
return state;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Count fields that differ from last confirmed state
|
|
45
|
+
let changedFieldCount = 0;
|
|
46
|
+
for ( let i = 0; i < state.fieldCount; i += 1 ) {
|
|
47
|
+
const field = state.fieldNames[ i ];
|
|
48
|
+
if ( state.prevValues[ field ] !== msg[ field ] ) {
|
|
49
|
+
changedFieldCount += 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Apply changeMode logic
|
|
54
|
+
const changeSpotted = state.changeMode ?
|
|
55
|
+
( changedFieldCount === state.fieldCount ) : // 'all': every field changed
|
|
56
|
+
( changedFieldCount > 0 ); // 'any': at least one changed
|
|
57
|
+
|
|
58
|
+
if ( changeSpotted ) {
|
|
59
|
+
// Different from last confirmed state
|
|
60
|
+
state.debounceCount += 1;
|
|
61
|
+
|
|
62
|
+
// Check if debounce threshold reached
|
|
63
|
+
if ( state.debounceCount >= state.debounce ) {
|
|
64
|
+
// Change CONFIRMED!
|
|
65
|
+
// Calculate stats BEFORE updating prevValues
|
|
66
|
+
state.dwellTime = timestamp - state.stateStartTime;
|
|
67
|
+
// A dwell can never be negative. A backward clock step
|
|
68
|
+
// (an NTP correction landing mid-measurement) would make
|
|
69
|
+
// the subtraction negative — publish 0 instead, matching
|
|
70
|
+
// dwellTimeTracker's guard (its update.js:74).
|
|
71
|
+
if ( state.dwellTime < 0 ) state.dwellTime = 0;
|
|
72
|
+
state.dwellSamples = state.samplesInState;
|
|
73
|
+
|
|
74
|
+
// Commit new state - update prevValues
|
|
75
|
+
for ( let i = 0; i < state.fieldCount; i += 1 ) {
|
|
76
|
+
const field = state.fieldNames[ i ];
|
|
77
|
+
state.prevValues[ field ] = msg[ field ];
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Reset counters for next transition
|
|
81
|
+
state.debounceCount = 0;
|
|
82
|
+
state.stateStartTime = timestamp;
|
|
83
|
+
state.samplesInState = 1;
|
|
84
|
+
|
|
85
|
+
// Execute triggers
|
|
86
|
+
executeTriggers( state );
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// Matches last confirmed state - reset debounce
|
|
90
|
+
state.debounceCount = 0;
|
|
91
|
+
state.samplesInState += 1;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return state;
|
|
95
|
+
}; // update()
|
|
96
|
+
|
|
97
|
+
export default update;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
// nodes/sw-stats/index.js
|
|
2
|
+
|
|
3
|
+
export { default as init } from './init.js';
|
|
4
|
+
export { default as update } from './update.js';
|
|
5
|
+
export { default as reset } from './reset.js';
|
|
6
|
+
export { default as recompute } from './recompute.js';
|
|
7
|
+
export { default as publishTo } from './publish-to.js';
|
|
8
|
+
// Direct imports for common control functions
|
|
9
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
10
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
11
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
12
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
13
|
+
// Re-export everything from introspection.js
|
|
14
|
+
export * from './introspect.js';
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// nodes/sw-stats/init.js
|
|
2
|
+
|
|
3
|
+
import { validateSpec } from '../../core/utils/node/index.js';
|
|
4
|
+
import * as introspect from './introspect.js';
|
|
5
|
+
import { create } from '../../windowing/count-sliding/index.js';
|
|
6
|
+
import { resolveScalar } from '../../core/utils/options/resolve-field-keyed.js';
|
|
7
|
+
|
|
8
|
+
const init = function ( spec ) {
|
|
9
|
+
validateSpec( spec, introspect );
|
|
10
|
+
|
|
11
|
+
// Create state after validation passes
|
|
12
|
+
const state = Object.create( null );
|
|
13
|
+
// Standard input parameter health flag during update: false means healthy
|
|
14
|
+
state.inputValidationFailed = false;
|
|
15
|
+
// Ensure node is enabled by default
|
|
16
|
+
state.disable = false;
|
|
17
|
+
state.pause = false;
|
|
18
|
+
|
|
19
|
+
// Extract field name.
|
|
20
|
+
state.x = spec.from.x;
|
|
21
|
+
|
|
22
|
+
// Apply defaults from introspect (validation doesn't enforce them)
|
|
23
|
+
// Supports both direct and field-keyed specification
|
|
24
|
+
const windowSizeSpec = resolveScalar( spec.windowSize, state.x );
|
|
25
|
+
const windowSize = windowSizeSpec ?? introspect.DEFAULT_OPTIONS.windowSize;
|
|
26
|
+
|
|
27
|
+
// Underlying ring buffer storing the windowed values.
|
|
28
|
+
state.ring = create( windowSize );
|
|
29
|
+
|
|
30
|
+
// Config copy.
|
|
31
|
+
state.stats = Object.assign( Object.create( null ), spec.stats );
|
|
32
|
+
|
|
33
|
+
// Which of the SUPPORTED_STATS the user actually requested.
|
|
34
|
+
const statsList = Object.keys( state.stats );
|
|
35
|
+
// Determine which moments we need to compute and save it as state constants
|
|
36
|
+
state.need2 = statsList.some( ( m ) => [ 'variance', 'stdev', 'skewness', 'kurtosis', 'rms' ].includes( m ) );
|
|
37
|
+
state.need3 = statsList.some( ( m ) => [ 'skewness', 'kurtosis' ].includes( m ) );
|
|
38
|
+
state.need4 = statsList.includes( 'kurtosis' );
|
|
39
|
+
|
|
40
|
+
// State variables for incremental computation (s=sum, n=power).
|
|
41
|
+
state.s1 = 0;
|
|
42
|
+
state.s2 = ( state.need2 ) ? 0 : undefined;
|
|
43
|
+
state.s3 = ( state.need3 ) ? 0 : undefined;
|
|
44
|
+
state.s4 = ( state.need4 ) ? 0 : undefined;
|
|
45
|
+
|
|
46
|
+
state.nodeType = introspect.getNodeType();
|
|
47
|
+
|
|
48
|
+
return state;
|
|
49
|
+
}; // init()
|
|
50
|
+
|
|
51
|
+
export default init;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
// nodes/sw-stats/introspect.js
|
|
2
|
+
|
|
3
|
+
import { validators } from '../../core/utils/validate/index.js';
|
|
4
|
+
|
|
5
|
+
/** Single source of truth for what this plugin can do */
|
|
6
|
+
const SUPPORTED_STATS = [
|
|
7
|
+
'mean',
|
|
8
|
+
'variance',
|
|
9
|
+
'stdev',
|
|
10
|
+
'skewness',
|
|
11
|
+
'kurtosis',
|
|
12
|
+
'rms'
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
/** Human-readable descriptions (for introspection) */
|
|
16
|
+
const STAT_DESCRIPTIONS = {
|
|
17
|
+
mean: 'Average of the last N samples',
|
|
18
|
+
variance: 'Sample variance (denominator N–1)',
|
|
19
|
+
stdev: 'Sample standard deviation',
|
|
20
|
+
skewness: 'Normalized third central moment',
|
|
21
|
+
kurtosis: 'Excess kurtosis (μ₄/σ⁴ – 3)',
|
|
22
|
+
rms: 'Root mean square'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Control methods available for triggers */
|
|
26
|
+
const SUPPORTED_CONTROL_METHODS = {
|
|
27
|
+
reset: 'Clears accumulated state and restarts computation',
|
|
28
|
+
enable: 'Enables node processing',
|
|
29
|
+
disable: 'Disables node processing',
|
|
30
|
+
pause: 'Pauses node processing while keeping state visible',
|
|
31
|
+
unpause: 'Resumes node processing after pause'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** The type of this node */
|
|
35
|
+
const NODE_TYPE = 'SW Stats';
|
|
36
|
+
|
|
37
|
+
/** Node capabilities for documentation */
|
|
38
|
+
const CAPABILITIES = {
|
|
39
|
+
description: 'Computes running statistics over a sliding window',
|
|
40
|
+
features: [
|
|
41
|
+
'Efficient incremental computation',
|
|
42
|
+
'Configurable window size',
|
|
43
|
+
'Multiple statistics in single pass',
|
|
44
|
+
'Numerically stable algorithms'
|
|
45
|
+
]
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Attach static metadata once, from our constants by making a shallow copy.
|
|
49
|
+
export const getSupportedStats = () => SUPPORTED_STATS.slice();
|
|
50
|
+
export const getStatDescriptions = () => ( { ...STAT_DESCRIPTIONS } );
|
|
51
|
+
export const getSupportedControlMethods = () => ( { ...SUPPORTED_CONTROL_METHODS } );
|
|
52
|
+
export const getNodeType = () => NODE_TYPE;
|
|
53
|
+
export const getCapabilities = () => ( { ...CAPABILITIES, features: CAPABILITIES.features.slice() } );
|
|
54
|
+
|
|
55
|
+
export const DEFAULT_OPTIONS = {
|
|
56
|
+
windowSize: 10
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// DSL Metadata for transpilation
|
|
60
|
+
const DSL_METADATA = {
|
|
61
|
+
// Spec schema for validation
|
|
62
|
+
specSchema: {
|
|
63
|
+
nodeType: {
|
|
64
|
+
type: 'string',
|
|
65
|
+
required: true,
|
|
66
|
+
value: NODE_TYPE
|
|
67
|
+
},
|
|
68
|
+
name: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
required: true,
|
|
71
|
+
validator: validators.identifier,
|
|
72
|
+
error: 'Name must be a valid identifier'
|
|
73
|
+
},
|
|
74
|
+
from: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
required: true,
|
|
77
|
+
properties: {
|
|
78
|
+
x: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
required: true,
|
|
81
|
+
validator: validators.noSpaces,
|
|
82
|
+
error: 'Field name cannot contain spaces'
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
windowSize: {
|
|
87
|
+
type: 'numberOrFieldKeyed',
|
|
88
|
+
required: false,
|
|
89
|
+
default: 10,
|
|
90
|
+
integer: true,
|
|
91
|
+
min: 4,
|
|
92
|
+
validator: validators.positiveInteger,
|
|
93
|
+
error: 'Window size must be a positive integer; minimum 4'
|
|
94
|
+
},
|
|
95
|
+
stats: {
|
|
96
|
+
type: 'object',
|
|
97
|
+
required: true,
|
|
98
|
+
minProperties: 1,
|
|
99
|
+
propertyNames: SUPPORTED_STATS,
|
|
100
|
+
propertySchema: {
|
|
101
|
+
type: 'object',
|
|
102
|
+
required: true,
|
|
103
|
+
properties: {
|
|
104
|
+
storeAs: {
|
|
105
|
+
type: 'string',
|
|
106
|
+
required: true,
|
|
107
|
+
minLength: 1,
|
|
108
|
+
validator: validators.identifier,
|
|
109
|
+
error: 'storeAs must be a valid identifier'
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
|
|
116
|
+
// Cross-field validators
|
|
117
|
+
crossFieldValidators: [],
|
|
118
|
+
|
|
119
|
+
// How to build the spec from DSL params
|
|
120
|
+
// Pattern: NAME_X_OUTPUTS_OPTIONS -> (name, x, stats, options)
|
|
121
|
+
buildSpec: ( name, x, stats, options ) => ( {
|
|
122
|
+
nodeType: NODE_TYPE,
|
|
123
|
+
name,
|
|
124
|
+
from: { x },
|
|
125
|
+
stats,
|
|
126
|
+
...options
|
|
127
|
+
} ),
|
|
128
|
+
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export const getDSLMetadata = () => ( { ...DSL_METADATA } );
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// nodes/sw-stats/publish-to.js
|
|
2
|
+
|
|
3
|
+
import { isNotFull, size } from '../../windowing/count-sliding/index.js';
|
|
4
|
+
import { publishNaN } from '../../core/utils/node/index.js';
|
|
5
|
+
|
|
6
|
+
const EPS = 1e-9;
|
|
7
|
+
|
|
8
|
+
const publishTo = function ( state, msg ) {
|
|
9
|
+
if ( state.disable ) return;
|
|
10
|
+
// Propagate NaN if unhealthy
|
|
11
|
+
if ( state.inputValidationFailed ) {
|
|
12
|
+
publishNaN( state, msg );
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
// Do not publish partial results.
|
|
16
|
+
if ( isNotFull( state.ring ) ) return;
|
|
17
|
+
|
|
18
|
+
const windowSize = size( state.ring );
|
|
19
|
+
const mean = state.s1 / windowSize;
|
|
20
|
+
if ( state.stats.mean ) msg[ state.stats.mean.storeAs ] = mean;
|
|
21
|
+
if ( state.need2 ) {
|
|
22
|
+
const sumSquaredDeviations = state.s2 - ( state.s1 * mean );
|
|
23
|
+
const m2 = sumSquaredDeviations / windowSize;
|
|
24
|
+
const variance = sumSquaredDeviations / ( windowSize - 1 );
|
|
25
|
+
const safeVariance = ( variance < EPS ) ? 0 : variance;
|
|
26
|
+
// --- Use `stats.variance` in the if statement to check ---------------------
|
|
27
|
+
if ( state.stats.variance ) msg[ state.stats.variance.storeAs ] = safeVariance;
|
|
28
|
+
if ( state.stats.stdev ) msg[ state.stats.stdev.storeAs ] = Math.sqrt( safeVariance );
|
|
29
|
+
if ( state.stats.rms ) msg[ state.stats.rms.storeAs ] = Math.sqrt( state.s2 / windowSize );
|
|
30
|
+
if ( state.need3 ) {
|
|
31
|
+
// Formula: m3 = ( s3 - 3 μ s2 + 2 μ^3 N ) / N
|
|
32
|
+
const m3 = ( state.s3 - (3 * mean * state.s2 ) + ( 2 * mean * mean * mean * windowSize ) ) / windowSize;
|
|
33
|
+
const denom = Math.pow( m2, 1.5 );
|
|
34
|
+
if ( state.stats.skewness ) msg[ state.stats.skewness.storeAs ] = ( denom > EPS ) ? m3 / denom : 0;
|
|
35
|
+
}
|
|
36
|
+
if ( state.need4 ) {
|
|
37
|
+
const m4 = ( state.s4 - ( 4 * ( mean * state.s3 ) ) + ( 6 * mean * mean * state.s2 ) -
|
|
38
|
+
( 3 * mean * mean * mean * mean * windowSize ) ) / windowSize;
|
|
39
|
+
const denom = m2 * m2;
|
|
40
|
+
if ( state.stats.kurtosis ) msg[ state.stats.kurtosis.storeAs ] = ( denom > EPS ) ? ( ( m4 / denom ) - 3 ) : -3;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}; // publishTo()
|
|
44
|
+
|
|
45
|
+
export default publishTo;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// nodes/sw-stats/recompute.js
|
|
2
|
+
|
|
3
|
+
const recompute = function ( state ) {
|
|
4
|
+
// Reset accumulators first
|
|
5
|
+
state.s1 = 0;
|
|
6
|
+
state.s2 = state.need2 ? 0 : undefined;
|
|
7
|
+
state.s3 = state.need3 ? 0 : undefined;
|
|
8
|
+
state.s4 = state.need4 ? 0 : undefined;
|
|
9
|
+
|
|
10
|
+
const buf = state.ring.buffer;
|
|
11
|
+
// Only process actual values
|
|
12
|
+
const used = state.ring.used;
|
|
13
|
+
|
|
14
|
+
for ( let k = 0; k < used; k += 1 ) {
|
|
15
|
+
const value = buf[ k ];
|
|
16
|
+
state.s1 += value;
|
|
17
|
+
if ( state.need2 || state.need3 || state.need4 ) {
|
|
18
|
+
const value2 = value * value;
|
|
19
|
+
if ( state.need2 ) state.s2 += value2;
|
|
20
|
+
if ( state.need3 || state.need4 ) {
|
|
21
|
+
const value3 = value2 * value;
|
|
22
|
+
if ( state.need3 ) state.s3 += value3;
|
|
23
|
+
if ( state.need4 ) {
|
|
24
|
+
const value4 = value3 * value;
|
|
25
|
+
state.s4 += value4;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}; // recompute()
|
|
32
|
+
|
|
33
|
+
export default recompute;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// nodes/sw-stats/reset.js
|
|
2
|
+
|
|
3
|
+
import { reset as resetRing } from '../../windowing/count-sliding/index.js';
|
|
4
|
+
const reset = function ( state ) {
|
|
5
|
+
state.s1 = 0;
|
|
6
|
+
state.s2 = state.need2 ? 0 : undefined;
|
|
7
|
+
state.s3 = state.need3 ? 0 : undefined;
|
|
8
|
+
state.s4 = state.need4 ? 0 : undefined;
|
|
9
|
+
|
|
10
|
+
resetRing( state.ring );
|
|
11
|
+
|
|
12
|
+
return true;
|
|
13
|
+
}; // reset()
|
|
14
|
+
|
|
15
|
+
export default reset;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// nodes/sw-stats/update.js
|
|
2
|
+
|
|
3
|
+
import { push } from '../../windowing/count-sliding/index.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
|
+
const evicted = push( state.ring, xVal ) ?? 0;
|
|
18
|
+
|
|
19
|
+
state.s1 += xVal - evicted;
|
|
20
|
+
if ( state.need2 ) {
|
|
21
|
+
const value2 = xVal * xVal;
|
|
22
|
+
const evicted2 = evicted * evicted;
|
|
23
|
+
state.s2 += value2 - evicted2;
|
|
24
|
+
if ( state.need3 ) {
|
|
25
|
+
const value3 = value2 * xVal;
|
|
26
|
+
const evicted3 = evicted2 * evicted;
|
|
27
|
+
state.s3 += value3 - evicted3;
|
|
28
|
+
if ( state.need4 ) {
|
|
29
|
+
const value4 = value3 * xVal;
|
|
30
|
+
const evicted4 = evicted3 * evicted;
|
|
31
|
+
state.s4 += value4 - evicted4;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return state;
|
|
37
|
+
}; // update()
|
|
38
|
+
|
|
39
|
+
export default update;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// @fileoverview
|
|
2
|
+
// index.js — standard node exports per ADR-004.
|
|
3
|
+
//
|
|
4
|
+
// Re-exports all lifecycle functions and shared control signals. The
|
|
5
|
+
// introspection metadata (getSupportedStats, getDSLMetadata, etc.) is
|
|
6
|
+
// re-exported via the wildcard export from introspect.js.
|
|
7
|
+
|
|
8
|
+
export { default as init } from './init.js';
|
|
9
|
+
export { default as update } from './update.js';
|
|
10
|
+
export { default as reset } from './reset.js';
|
|
11
|
+
export { default as recompute } from './recompute.js';
|
|
12
|
+
export { default as publishTo } from './publish-to.js';
|
|
13
|
+
export { default as disable } from '../../core/utils/node/disable.js';
|
|
14
|
+
export { default as enable } from '../../core/utils/node/enable.js';
|
|
15
|
+
export { default as pause } from '../../core/utils/node/pause.js';
|
|
16
|
+
export { default as unpause } from '../../core/utils/node/unpause.js';
|
|
17
|
+
export * from './introspect.js';
|